ProgressDialog kullanımdan kaldırıldı. Alternatif olan nedir?


215

Bunun ProgressDialogartık kullanımdan kaldırıldığını görmek için karşılaştım . Bunun dışında kullanmak için alternatif olan ne olurdu ProgressBar. Android studio 2.3.3 sürümünü kullanıyorum.

ProgressDialog progressDialog=new ProgressDialog(this);
progressDialog.show();

Yanıtlar:


196

Evet, API level 26kullanımdan kaldırıldı. Bunun yerine kullanabilirsiniz progressBar.

Programlı olarak oluşturmak için:

Önce kök düzenine bir referans alın

RelativeLayout layout = findViewById(R.id.display);  //specify here Root layout Id

veya

RelativeLayout layout = findViewById(this);

Ardından ilerleme çubuğunu ekleyin

progressBar = new ProgressBar(youractivity.this, null, android.R.attr.progressBarStyleLarge);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
layout.addView(progressBar, params);

İlerleme çubuğunu göstermek için

progressBar.setVisibility(View.VISIBLE);

İlerleme çubuğunu gizlemek için

progressBar.setVisibility(View.GONE);

Kullanıcı etkileşimini devre dışı bırakmak için aşağıdaki kodu eklemeniz yeterlidir

getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                           WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

Kullanıcı etkileşimini geri almak için aşağıdaki kodu eklemeniz yeterlidir

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

Yalnızca ileride android.R.attr.progressBarStyleSmallbaşvurmak üzere olarak değiştirin android.R.attr.progressBarStyleHorizontal.

Aşağıdaki kod sadece yukarıda çalışır API level 21

progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));

Xml ile oluşturmak için:

<ProgressBar
        android:id="@+id/progressbar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        android:max="100"
        android:backgroundTint="@color/white"
        android:layout_below="@+id/framelauout"
        android:indeterminateTint="#1a09d6"
        android:layout_marginTop="-7dp"/>

Faaliyetinizde

progressBar = (ProgressBar) findViewById(R.id.progressbar);

İlerleme çubuğunu göstermek / gizlemek aynı

 progressBar.setVisibility(View.VISIBLE); // To show the ProgressBar 
 progressBar.setVisibility(View.INVISIBLE); // To hide the ProgressBar

İşte neye benzeyeceğine dair örnek bir resim:

Daha fazla ayrıntı için:
1. Birinci
Referans 2. İkinci Referans


Sanki bunu kullanıyorsunuz ve evet gibi ilerliyorsunuz. Son olarak ilerleme iletişim kutusu kullanımdan kaldırıldı, bu yüzden dünyaya potansiyelimi gösterebilirim: D, şaka yapıyorum, bu ayrıntılı cevap için çok teşekkürler.
Mohamed Hajr

1
ama setMessage () nasıl?
reverie_ss


lütfen xml kodunu "@android: renkli / beyaz" olarak güncelleyin
musterjunk

Yatay Çubuğu almak için lütfen şunu kullanın: ProgressBar progressBar = new ProgressBar (youractivity.this, null, android.R.attr.progressBarStyleHorizontal); progressBar.setIndeterminate (doğru);
taranjeetsapra

44

aşağıdaki kod için referans AlertDialogolarak kullanabilirsiniz . Bir ilerleme iletişim kutusu gösterdiğinizde çağırmanız gereken bu işlev.ProgressDialogProgressDialog

Kod:

    public void setProgressDialog() {

    int llPadding = 30;
    LinearLayout ll = new LinearLayout(this);
    ll.setOrientation(LinearLayout.HORIZONTAL);
    ll.setPadding(llPadding, llPadding, llPadding, llPadding);
    ll.setGravity(Gravity.CENTER);
    LinearLayout.LayoutParams llParam = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    llParam.gravity = Gravity.CENTER;
    ll.setLayoutParams(llParam);

    ProgressBar progressBar = new ProgressBar(this);
    progressBar.setIndeterminate(true);
    progressBar.setPadding(0, 0, llPadding, 0);
    progressBar.setLayoutParams(llParam);

    llParam = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
    llParam.gravity = Gravity.CENTER;
    TextView tvText = new TextView(this);
    tvText.setText("Loading ...");
    tvText.setTextColor(Color.parseColor("#000000"));
    tvText.setTextSize(20);
    tvText.setLayoutParams(llParam);

    ll.addView(progressBar);
    ll.addView(tvText);

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setCancelable(true);
    builder.setView(ll);

    AlertDialog dialog = builder.create();
    dialog.show();
    Window window = dialog.getWindow();
    if (window != null) {
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        layoutParams.copyFrom(dialog.getWindow().getAttributes());
        layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
        layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
        dialog.getWindow().setAttributes(layoutParams);
    }
}

Çıktı:

resim açıklamasını buraya girin


Nasıl reddedilir?
Jay Dangar

1
BaseActivity gibi bir temel sınıf yapın, daha sonra bu kod parçasını ekledikten sonra, bir yöntem reddetme iletişim kutusu ekleyin, ancak bunun için küresel olarak 'AlertDialog iletişim' nesnesini tanımlamanız gerekir
Kishan Donga

daha sonra gizle iletişim yönteminizin içinde dialog.dismiss () yöntemini çağırmanız yeterlidir.
Kishan Donga

1
Her şey yolunda ama ekran iletişim kutusunda herhangi bir yere dokunarak işten çıkarılıyor.
Vivek Kumar

İptal edilemez olarak ayarlayabilirsiniz, ancak cihazı döndürmek veya bölünmüş ekranı kullanmak yine de kapatılır ve kullanıcı hazır olmayan kullanıcı arayüzüyle etkileşime girebilir. Belki AlertDialogburada kullanılmamalıdır.
Miyav Kedi 2012

30

Bu sınıf API 26 düzeyinde kullanımdan kaldırılmıştır. ProgressDialog, kullanıcının uygulama ile etkileşimini engelleyen kalıcı bir iletişim kutusudur. Bu sınıfı kullanmak yerine, uygulamanızın kullanıcı arayüzüne yerleştirilebilen ProgressBar gibi bir ilerleme göstergesi kullanmalısınız. Alternatif olarak, kullanıcıyı görevin ilerleme durumu hakkında bilgilendirmek için bir bildirim kullanabilirsiniz. bağlantı

Yeni kullanıcı arayüzü standardı Android Onedeniyle kullanımdan kaldırıldıGoogle


1
Bir tarih yerine "Bu sınıf API 26 düzeyinde kullanımdan kaldırıldı" kullanılıyor. Artık kullanmanı istemiyorlar. Evet, yeni kullanıcı arayüzü standartları nedeniyle, ancak her API düzeyi için geçerlidir ...
creativecreatorormaybenot

28
"Google yeni UI standardı nedeniyle Android O'da kullanımdan kaldırıldı" - bağlantı lütfen yeni UI standardı
Ajay S

22
Ve bunu reddetmenin nedeni, kullanıcı girişini engellemesidir. Bazen Google mühendislerinin ne tür bir toz yaktığını merak ediyorum. Engelleme iletişim kutusunu kullanmak, giriş denetimlerini gizlemek / devre dışı bırakmak ve ardından bir ilerleme çubuğunu etkinleştirmek yerine çok daha kolaydır. Google adına aptalca bir düşünce.
TheRealChx101

@ TheRealChx101 Android UI, kullanıcı, ondan kaynaklanan ölümcül hataları önlemek için pahalı geçici çözümler pahasına yanlış bir özgürlük hissi hissettirmek için tasarlanmıştır. İzinleri alana ve operatörlerin Google'a erişebileceğiniz ve erişemeyeceğinizi söylemesi için ne kadar ödediğini bekleyin.
Terk Edilmiş Araba

3
Sanırım modal diyalogların kötüye kullanımını önlemekle daha fazla ilgileniyorlar: bazen kullanıcının hiç beklemesi gerekmediği, ancak başka şeyler yapmaya devam edebileceği durumlarda kullanıcıyı bir işlemin sonunda bekletmesi gösteriliyor , bu nedenle onları gereksiz yere bekletmek kullanıcı deneyimini bozar. Bununla birlikte, kullanıcı için başka seçeneğin olmadığı, ancak bir işlemin tamamlanmasını beklediği birçok koşul vardır, bu yüzden her geliştiriciyi kendi uygulamalarını uygulamak yerine bu durumlar için kullanımdan kaldırılmamış standart bir yöntem olması gerektiğini kabul ediyorum.
Fran Marzoa

26

İlerleme çubuğunuz için bir xml arayüzü tasarlayabilir ve bir AlertDialog'a görünüm olarak aktarabilir, ardından istediğiniz zaman iletişim kutusunu gösterebilir veya kapatabilirsiniz.

progress.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:padding="13dp"
    android:layout_centerHorizontal="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ProgressBar
        android:id="@+id/loader"
        android:layout_marginEnd="5dp"
        android:layout_width="45dp"
        android:layout_height="45dp" />
    <TextView
        android:layout_width="wrap_content"
        android:text="Loading..."
        android:textAppearance="?android:textAppearanceSmall"
        android:layout_gravity="center_vertical"
        android:id="@+id/loading_msg"
        android:layout_toEndOf="@+id/loader"
        android:layout_height="wrap_content" />

</LinearLayout>

İlerleme durumu iletişim kutusunu gösteren kod kodu. Sadece bu kodu kopyalayın ve parçanızı yapıştırın.

  AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
       private void setDialog(boolean show){
            builder.setView(R.layout.progress);
            Dialog dialog = builder.create();
            if (show)dialog.show();
            else dialog.dismiss();
        }

Sonra progressdialog'u göstermek istediğinizde yöntemi çağırın ve bunu göstermek için argüman olarak true değerini veya iletişim kutusunu kapatmak için false değerini iletin.


2
@Alok Rajasukumaran, etkinliği kullandığınız anlaşılıyor, sadece thisbununla görünecek şekilde değiştirin.Lütfen AlertDialog.Builder builder = new AlertDialog.Builder(this);doğru kodu kopyaladığınızdan emin olun.
DevMike01

1
layout_toEndOfkaldırılmadığı için kaldırılabilirLinearLayout
Mark

21

ProgressBar çok basit ve kullanımı kolaydır, bunu basit ilerleme iletişim kutusu ile aynı yapmak niyetindeyim. ilk adım, göstermek istediğiniz iletişim kutusunun xml düzenini yapabilmenizdir.

layout_loading_dialog.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="20dp">
    <ProgressBar
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="4"
        android:gravity="center"
        android:text="Please wait! This may take a moment." />
</LinearLayout>

sonraki adım, ProgressBar ile bu düzeni gösterecek AlertDialog'u oluşturmaktır

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(false); // if you want user to wait for some process to finish,
builder.setView(R.layout.layout_loading_dialog);
AlertDialog dialog = builder.create();

şimdi geriye kalan tek şey, bu gibi tıklama etkinliklerinde bu iletişim kutusunu göstermek ve gizlemek.

dialog.show(); // to show this dialog
dialog.dismiss(); // to hide this dialog

ve işte bu, ProgressDialog yerine ProgressBar'ı uygulamanın oldukça basit ve kolay olduğunu görebileceğiniz gibi çalışmalıdır. artık bu iletişim kutusunu Handler veya ASyncTask'ta gösterebilir / kapatabilirsiniz, ihtiyacınıza göre


, Değiştirin progress_dialog.show();için builder.show();. Teşekkür ederim.
Matheus Miranda

2
Adı doğru AlertDialog nesnesi olarak değiştirdim, show()/ dismiss(), ama koymadım builder.show()çünkü o zaman reddetmek için dialog.dismiss(), kodunu basit ve kolay anlaşılır tutmak isteyen biri için kafa karıştırıcı bir şey yapmak zorunda kalacağız. çünkü nesneyi builder.show()döndürür AlertDialogve bu referansı o AlertDialog dialog = builder.show();zamanki gibi kaydetmeliyiz, bazı nesnelerde geri dönmek dialog.dismiss()daha kolaydır ve daha sonra iletişim kutusunu göstermek ve gizlemek için bu nesneyi kullanırızbuilder.create()AlertDialog
Syed Naeem

Çağıran evre Ana Kullanıcı Arabirimi İş parçacığı değilse "runOnUiThread (new Runnable () {@Override public void run () {dialog.show/dismiss ();}});" (WebView - shouldoverride yöntemleri çağrıldığında kullanışlıdır.)
SHS

15

Evet, ProgressDialog kullanımdan kaldırıldı, ancak Dialog desteklenmiyor.

Kendi XML dosyanızı (ilerleme çubuğu ve yükleme metni içeren) diyalog nesnenize şişirebilir ve sonra show()ve dismiss()işlevlerini kullanarak dosyayı görüntüleyebilir veya gizleyebilirsiniz . İşte bir örnek (Kotlin):

ProgressDialog sınıfı :

class ProgressDialog {
companion object {
    fun progressDialog(context: Context): Dialog{
        val dialog = Dialog(context)
        val inflate = LayoutInflater.from(context).inflate(R.layout.progress_dialog, null)
        dialog.setContentView(inflate)
        dialog.setCancelable(false)
        dialog.window!!.setBackgroundDrawable(
                ColorDrawable(Color.TRANSPARENT))
        return dialog
    }
  }
}

XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:background="#fff"
android:padding="13dp"
android:layout_height="wrap_content">
<ProgressBar
    android:id="@+id/progressBar"
    style="?android:attr/progressBarStyle"
    android:layout_width="100dp"
    android:layout_margin="7dp"
    android:layout_height="100dp"
    android:layout_above="@+id/no_jobs_pickups"/>
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_margin="7dp"
    android:layout_toEndOf="@+id/progressBar"
    android:text="Loading..." />
</RelativeLayout>

Kodunuzda : Sadece yapınvar dialog = ProgressDialog.progressDialog(context)

Göstermek için: dialog.show()

Gizlemek için: dialog.dismiss()


11

Eğer gerçekten onların iradesine karşı çıkmak istiyorsanız, yine de Tatlı Uyarı İletişim Kutusunu kullanabilir veya kendi başınıza bir tane oluşturabilirsiniz.

progress_dialog_layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TableRow
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="64dp" >

        <ProgressBar
            android:id="@+id/progressBar2"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />

        <TextView
            android:gravity="center|left"
            android:id="@+id/textView9"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:text="Downloading data. Please wait.." />
    </TableRow>
</RelativeLayout>

Java kodu:

AlertDialog b;
AlertDialog.Builder dialogBuilder;

public void ShowProgressDialog() {
dialogBuilder = new AlertDialog.Builder(DataDownloadActivity.this);
LayoutInflater inflater = (LayoutInflater) getSystemService( Context.LAYOUT_INFLATER_SERVICE );
            View dialogView = inflater.inflate(R.layout.progress_dialog_layout, null);
            dialogBuilder.setView(dialogView);
            dialogBuilder.setCancelable(false);
            b = dialogBuilder.create();
            b.show();
        }

        public void HideProgressDialog(){

            b.dismiss();
        }

2
Bir üretim ortamında SweetAlertDialog ile korkunç deneyimler yaşadım, pencereden pencereye sızdığı için çok dikkatli olun.
Jeremy

benim için HideProgresshiçbir şey yapmaz.
Saeed Neamati

1
Neden yalnızca ilerleme durumu iletişim kutusunu göstermek için bir kitaplık kullanılmalı?
Khemraj


6

Herhangi bir özel kitaplığı içe aktarmanıza gerek yoktur.

Modern kullanmayı tercih ediyorum, AlertDialogbu yüzden Kishan Donga'nın bu sayfada gönderdiği harika cevap için Kotlin versiyonu .

Kotlin kodu:

fun setProgressDialog(context:Context, message:String):AlertDialog {
    val llPadding = 30
    val ll = LinearLayout(context)
    ll.orientation = LinearLayout.HORIZONTAL
    ll.setPadding(llPadding, llPadding, llPadding, llPadding)
    ll.gravity = Gravity.CENTER
    var llParam = LinearLayout.LayoutParams(
                  LinearLayout.LayoutParams.WRAP_CONTENT,
                  LinearLayout.LayoutParams.WRAP_CONTENT)
    llParam.gravity = Gravity.CENTER
    ll.layoutParams = llParam

    val progressBar = ProgressBar(context)
    progressBar.isIndeterminate = true
    progressBar.setPadding(0, 0, llPadding, 0)
    progressBar.layoutParams = llParam

    llParam = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT)
    llParam.gravity = Gravity.CENTER
    val tvText = TextView(context)
    tvText.text = message
    tvText.setTextColor(Color.parseColor("#000000"))
    tvText.textSize = 20.toFloat()
    tvText.layoutParams = llParam

    ll.addView(progressBar)
    ll.addView(tvText)

    val builder = AlertDialog.Builder(context)
    builder.setCancelable(true)
    builder.setView(ll)

    val dialog = builder.create()
    val window = dialog.window
    if (window != null) {
        val layoutParams = WindowManager.LayoutParams()
        layoutParams.copyFrom(dialog.window?.attributes)
        layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT
        layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT
                dialog.window?.attributes = layoutParams
    }
    return dialog
}

Kullanımı:

val dialog = setProgressDialog(this, "Loading..")
dialog.show()

Çıktı:

resim açıklamasını buraya girin


fragman nasıl kullanılır? parçadaki bağlam nedir?
roghayeh hosseini

Genellikle parçanızda mevcut görüşünüzü alabilirsiniz, bu yüzden bağlam şöyle olmalıdır:view!!.context
Alessandro Ornano

4

Üzerinde belirtildiği gibi dokümantasyon sayfasından alternatif olduğunu ProgressBar . ProgressDialog'ın görünümü a ProgressBariçine yerleştirilerek çoğaltılabilir AlertDialog.

Yine de kullanabilirsiniz, ancak Android bunu kullanmanızı istemez, bu yüzden kullanımdan kaldırılmıştır. Eğer bir gömme gibi, başka bir şekilde sorununuzu çözme düşünmelisiniz Yani ProgressBarSİZİN içine Layout.


@PeterHaddad Projemde api 28'de "normal" olarak çalışıyor ve alt apis'dekiyle aynı. Kullanımdan kaldırıldığı gibi, artık desteklenmemektedir ve gelecekte çalışmayabilir.
DMonkey

3

ProgressDialog API düzeyi 26'da kullanımdan kaldırıldı.

"Deprecated" "Yeni" terimi, yenileriyle değiştirilme sürecinde olan işlevler veya öğeler anlamına gelir.

ProgressDialog, kullanıcının uygulama ile etkileşimini engelleyen kalıcı bir iletişim kutusudur. Bu sınıfı kullanmak yerine, ProgressBaruygulamanızın kullanıcı arayüzüne yerleştirilebilecek bir ilerleme göstergesi kullanmalısınız .

avantaj

Şahsen ben ProgressBarbu iki kenarı vardır söyleyebilirim . ProgressBarbir işlemin ilerlemesini gösteren bir kullanıcı arabirim öğesidir. İlerleme çubuklarını bir kullanıcıya kesintisiz olarak görüntüleyin . İlerleme çubuğunu uygulamanızın kullanıcı arayüzünde gösterin.


2

Https://github.com/Q115/DelayedProgressDialog adresinden DelayedProgressDialog kullanıyorum .

Android O'dan önceki ProgressDialog uygulamasına benzer:

DelayedProgressDialog progressDialog = new DelayedProgressDialog();
progressDialog.show(getSupportFragmentManager(), "tag");

2

Yazdığım bu sınıfı kullanabilirsiniz . Yalnızca temel işlevleri sunar. Tamamen işlevsel bir ProgressDialog istiyorsanız, bu hafif kütüphaneyi kullanın .

Gradle Kurulumu

Module / build.gradle öğesine aşağıdaki bağımlılığı ekleyin:

compile 'com.lmntrx.android.library.livin.missme:missme:0.1.5'

Bu nasıl kullanılır?

Kullanım orijinal ProgressDialog'a benzer

ProgressDialog progressDialog = new 
progressDialog(YourActivity.this);
progressDialog.setMessage("Please wait");
progressDialog.setCancelable(false);
progressDialog.show();
progressDialog.dismiss();

Not : Etkinliklerin geçersiz kılınması gerekironBackPressed()

Java8 Uygulaması:

@Override
public void onBackPressed() {
    progressDialog.onBackPressed(
            () -> {
                super.onBackPressed();
                return null;
            }
    );
}

Kotlin Uygulaması:

override fun onBackPressed() {
   progressDialog.onBackPressed { super.onBackPressed() }
}

1

Belki bu rehber size yardımcı olabilir.

Genellikle göstergelerle özel AlertDialoglar yapmayı tercih ederim. Uygulama görünümünün özelleştirilmesi gibi sorunları çözer.


1

Diğer insanlara yardımcı olabilir.

Çok sayıda popüler uygulama, ağ isteği, dosya yükleme vb. Gibi herhangi bir şeyin ilerlemesini göstermek için farklı yaklaşımlara sahiptir. UI / UX açısından kötü olan bir belirsizlik dönemi vardır. Çok sayıda popüler uygulama (Facebook, Linkedin vb.) Önce UI'nin gösterdiği çıplak kemikleri göstererek bu sorunu çözdü. Ardından, yüklenen içerik yavaş yavaş ekrana yerleştirilir.

Bu sorunu çözmek için uygulamalarım için ışıltı kullandım .

Bununla ilgili başka insanlar için faydalı olacak iyi bir makale var


0

İlerleme durumu iletişim kutusunda, kullanıcı herhangi bir iş yapamaz. İlerleme durumu iletişim kutusu sırasında tüm arka plan işlemleri durdurulur. Bu nedenle, ilerleme iletişim kutusu yerine kullanıcının ilerleme çubuğunun kullanılması önerilir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.