Alterando a cor da linha inferior do EditText com o appcompat v7


310

Estou usando o appcompat v7 para obter uma aparência consistente no Android 5 e menos. Funciona bastante bem. No entanto, não consigo descobrir como alterar a cor da linha de fundo e a cor de destaque dos EditTexts. É possível?

Tentei definir um personalizado android:editTextStyle(cf. abaixo), mas só consegui alterar a cor completa do plano de fundo ou a cor do texto, mas não a linha inferior nem a cor de destaque. Existe um valor de propriedade específico para usar? eu tenho que usar uma imagem drawable personalizada através da android:backgroundpropriedade? não é possível especificar uma cor em hexa?

 <style name="Theme.App.Base" parent="Theme.AppCompat.Light.DarkActionBar">
     <item name="android:editTextStyle">@style/Widget.App.EditText</item>
 </style>

 <style name="Widget.App.EditText" parent="Widget.AppCompat.EditText">
     ???
 </style>

De acordo com as fontes da API 21 do Android, os EditTexts com design de material parecem usar colorControlActivatede colorControlNormal. Portanto, tentei substituir essas propriedades na definição de estilo anterior, mas ela não tem efeito. Provavelmente appcompat não o usa. Infelizmente, não consigo encontrar as fontes para a última versão do appcompat com design de material.


defina o seu tema para editar texto
Meenal 26/10

Obrigado por sua sugestão, mas eu já fiz isso. Atualizei minha pergunta para mostrar o que já tentei fazer. Meu problema é sobre o atributo a ser usado no estilo do tema para alterar a cor da linha inferior do edittext. Idealmente, estou procurando uma solução em que possa especificar diretamente a cor em hexa.
Laurent

Nenhuma dessas respostas está funcionando para mim no 4.3. Você tem uma solução funcional?
Robert Baker

Eu tive que estender AppCompatEditText, aparentemente.
EpicPandaForce

Respostas:


479

Finalmente, encontrei uma solução. Ela simplesmente consiste em substituir o valor para colorControlActivated, colorControlHighlighte colorControlNormalem sua definição de tema aplicativo e não o seu estilo EditText. Em seguida, pense em usar esse tema para qualquer atividade que desejar. Abaixo está um exemplo:

<style name="Theme.App.Base" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorControlNormal">#c5c5c5</item>
    <item name="colorControlActivated">@color/accent</item>
    <item name="colorControlHighlight">@color/accent</item>
</style>

9
Vejo que a cor do hambúrguer de menu, do botão Voltar e da barra de rolagem mudou para colorControlNormal.
Ario

11
Isso muda a cor da linha inferior, mas também altera todos os outros controles, como caixas de seleção, botões de opção etc. Também altera todos os EditText em todo o Aplicativo / Atividade. Se você quiser alterar a linha de fundo de um único EditText (como eu), substitua a propriedade android: background desse EditText. Não há outro jeito do AFAIK.
Emanuel Moecklin

68
Se você estiver usando a biblioteca de suporte v22 AppCompat, você pode especificar o tema na EditText como: android:theme="@style/Theme.App.Base. Isso garantirá que o estilo também não afete outras visualizações em seus layouts que você não deseja alterar.
Alex Lockwood

8
Não funcionou no Jelly Bean (4.2), mas trabalhou em Lollipop
Mangesh

3
A atividade deve herdar de AppCompatActivity. Não funcionará se herdar de Activity.
Bharathwaaj 12/01

187

Eu senti que isso precisava de uma resposta, caso alguém quisesse mudar apenas um único texto. Eu faço assim:

editText.getBackground().mutate().setColorFilter(ContextCompat.getColor(context, R.color.your_color), PorterDuff.Mode.SRC_ATOP);

1
Mas como posso redefinir para o plano de fundo padrão, tenho muitos erros depois de chamar editText.getBackground (). ResetColorFilter () no Lollipop?
ultraon 9/06/15

Cuidado! adicionar isso pode causar a alteração da cor do editText em TODOS os aplicativos. Testado no Nexus 5. OS 6.0.1.
Alex Perevozchykov 04/04

1
@AlexPerevozchykov, você pode estar certo. Tente adicionar .mutate () depois de .getBackground (). Isso deve resolver.
hordurh

@hordurh sim, era isso, eu recebi esta pequena sugestão pouco mais cedo, drawables compartilhar uma piscina para que antes de mudar precisamos transformar isso
Alex Perevozchykov

1
Se eu usar o edittext, é um trabalho, mas esse método não funciona com o app compat edittext no pirulito. você sabe como posso resolver o problema?
Selin

145

Embora a solução Laurents esteja correta, ela vem com algumas desvantagens, conforme descrito nos comentários, pois não apenas a linha inferior da EditTexttonalidade é tingida, mas também o botão Voltar da Toolbar, CheckBoxesetc.

Felizmente v22.1das appcompat-v7introduziu algumas novas possibilidades. Agora é possível atribuir um tema específico apenas a uma visualização. Diretamente do Changelog :

Uso preterido do app: tema para estilizar a Barra de Ferramentas. Agora você pode usar o android: theme para barras de ferramentas em todos os dispositivos API nível 7 e superiores e o suporte android: theme para todos os widgets nos dispositivos API level 11 e superiores.

Portanto, em vez de definir a cor desejada em um tema global, criamos um novo e o atribuímos apenas ao EditText.

Exemplo:

<style name="MyEditTextTheme">
    <!-- Used for the bottom line when not selected / focused -->
    <item name="colorControlNormal">#9e9e9e</item>
    <!-- colorControlActivated & colorControlHighlight use the colorAccent color by default -->
</style>

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/MyEditTextTheme"/>

1
Eu não acho que isso seja necessário. colorControlNormalsem o prefixo do Android, o método appcompat é usado para colorir os widgets, enquanto o prefixo recai no método do sistema e isso só está disponível nos dispositivos API 21+.
reverso

hmm, eu testei no meu dispositivo pirulito e ele não tingirá com a cor personalizada, a menos que você o implemente nos estilos v21 com o namespace android também.
User2968401

Eu estou usando 22.2.0a versão de AppCompat mas este truque não está funcionando :(
Shajeel Afzal

1
usando <item name = "colorAccent"> # 000000 </item> funcionou para mim em vez de colorControlNormal.
precisa saber é o seguinte

Perfeito para alterar apenas um texto de edição no aplicativo! (Exatamente o que eu precisava! Obrigado)
Romain Barbier

99

Isso pode ser alterado em XML usando:

Para a API de referência> = 21, use:

android:backgroundTint="@color/blue"

Para compatibilidade com API anterior <21, use:

app:backgroundTint="@color/blue"

10
Sim, no entanto, isso funciona apenas para as versões 21+ da API do Android (kitkat e lolipop). Alguém provavelmente tentaria criar uma solução compatível com versões anteriores.
DaMachk 03/04

12
Basta usar backgroundTint="@color/blue"em vez de android:backgroundTint="@color/blue"para o suporte compatível para trás
Kishan Vaghela

11
@KishanVaghela, em vez de usar android:backgroundTint="@color/blue", use app:backgroundTint="@color/blue"para suportar dispositivos pré-pirulito, obrigado pelo seu comentário!
blueware 27/09/16

2
@blueware Se você quiser usá-lo em grande estilo, não precisamos usar o "aplicativo".
Kishan Vaghela 27/09/16

adicione tools:ignore="MissingPrefix"se um sublinhado vermelho aparecer em #app:backgroundTint="<your color>"
Moses Aprico 13/17

47

Aqui está a solução para API <21 e acima

Drawable drawable = yourEditText.getBackground(); // get current EditText drawable 
drawable.setColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP); // change the drawable color

if(Build.VERSION.SDK_INT > 16) {
    yourEditText.setBackground(drawable); // set the new drawable to EditText
}else{
    yourEditText.setBackgroundDrawable(drawable); // use setBackgroundDrawable because setBackground required API 16
}

insira a descrição da imagem aqui

Espero que ajude


@CoolMind desculpe pela resposta tardia. Tenho verificar este código novamente na biblioteca de suporte 24.2.1, 25.1.1, 25.2.0para o dispositivo <21 e> 21 e ainda trabalhando. Verifique esta unidade de demonstração simples.google.com/file/d/0B_poNaia6t8kSzU3bDFVazRSSDA/… . Não sei por que esse código não funciona para você, por favor me avise. obrigado
Phan Van Linh

@Renjith pena de resposta tardia, por favor, verifique o meu comentário acima
Phan Van Linh

Como você redefine a cor para o padrão com este código?
Neph

35

A resposta aceita é um pouco mais por estilo, mas o mais eficiente é adicionar o atributo colorAccent ao seu estilo AppTheme da seguinte maneira:

<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:editTextStyle">@style/EditTextStyle</item>
</style>

<style name="EditTextStyle" parent="Widget.AppCompat.EditText"/>

O atributo colorAccent é usado para colorir o widget em todo o aplicativo e, portanto, deve ser usado para garantir a consistência


8
@Tomasz isso não acontecer requre API 21+ porque o tema pai é AppCompat que desce a API 7
TanmayP

Ughhh. Obrigado por isso, eu estava usando name="android:colorAccent"erroneamente
Jules

29

Se você estiver usando, appcompat-v7:22.1.0+poderá usar o DrawableCompat para colorir seus widgets

    public static void tintWidget(View view, int color) {
        Drawable wrappedDrawable = DrawableCompat.wrap(view.getBackground());
        DrawableCompat.setTint(wrappedDrawable.mutate(), getResources().getColor(color));
        view.setBackgroundDrawable(wrappedDrawable);
    }

o que um salva-vidas isto é incrível, uma vergonha não é bem sabe
pt123

Por favor, desça e veja minha resposta para a biblioteca de projetos 23.2.0. Este método é inútil no momento.
ywwynm

21

Usar:

<EditText
    app:backgroundTint="@color/blue"/>

Isso suportará dispositivos pré-pirulito não apenas +21


@ powder366, você pode postar o tema principal do seu aplicativo?
blueware

1
<style name = "AppTheme" parent = "Theme.AppCompat.Light.DarkActionBar">
powder366

2
@ powder366, tente o seguinte: <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">e ele deve funcionar
#

19

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>

    <item name="colorControlNormal">@color/colorAccent</item>
    <item name="colorControlActivated">@color/colorAccent</item>
    <item name="colorControlHighlight">@color/colorAccent</item>

</style>


Funciona! Como a pergunta faz, ela deve ser resolvida no estilo / tema, e deve ser a resposta correta.
Ninja

Concordo. esta deve ser a resposta correta, uma vez que é uma solução global
Santacrab

Isso é inconveniente se controles diferentes precisarem de cores diferentes.
Julius

12

Uma solução rápida para o seu problema é procurar em yourappspackage / build / intermediates / exploded-aar / com.android.support / appcompat-v7 / res / drawable / abc_edit_text_material.xml e copiar esse arquivo xml na sua pasta de desenho. Em seguida, você pode alterar a cor dos 9 arquivos de patch dentro deste seletor, para corresponder às suas preferências.


9

É muito fácil, basta adicionar android:backgroundTintatributo no seu EditText.

android:backgroundTint="@color/blue"
android:backgroundTint="#ffffff"
android:backgroundTint="@color/red"


 <EditText
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:backgroundTint="#ffffff"/>

2
backgroundTint acima da API nível 21 #
Ashwin H #

ViewCompat.setBackgroundTintList (EditText, ColorStateList.valueOf (Color.YELLOW))
Arst

1
app:backgroundTint="@color/blue"funciona em todos os níveis da API .. embora por qualquer motivo ele ainda está se voltando para uma cor diferente quando selecionado XD
EpicPandaForce

8

Aqui está uma parte do código-fonte da TextInputLayoutbiblioteca de design de suporte ( ATUALIZADO para a versão 23.2.0 ), que altera EditTexta cor da linha inferior de uma maneira mais simples:

private void updateEditTextBackground() {
    ensureBackgroundDrawableStateWorkaround();

    final Drawable editTextBackground = mEditText.getBackground();
    if (editTextBackground == null) {
        return;
    }

    if (mErrorShown && mErrorView != null) {
        // Set a color filter of the error color
        editTextBackground.setColorFilter(
                AppCompatDrawableManager.getPorterDuffColorFilter(
                        mErrorView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
    }
    ...
}

Parece que todo o código acima se torna inútil no momento na 23.2.0, se você deseja alterar a cor programaticamente.

E se você deseja suportar todas as plataformas, aqui está o meu método:

/**
 * Set backgroundTint to {@link View} across all targeting platform level.
 * @param view the {@link View} to tint.
 * @param color color used to tint.
 */
public static void tintView(View view, int color) {
    final Drawable d = view.getBackground();
    final Drawable nd = d.getConstantState().newDrawable();
    nd.setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter(
            color, PorterDuff.Mode.SRC_IN));
    view.setBackground(nd);
}

O que é o AppCompatDrawableManager? setBackground requer API 16. #
CoolMind 22/02

8

Eu também fiquei preso nesse problema por muito tempo.

Eu exigia uma solução que funcionasse para versões acima e abaixo da v21.

Finalmente descobri uma solução muito simples, talvez não ideal, mas eficaz: basta definir a cor do plano de fundo transparentnas propriedades EditText.

<EditText
    android:background="@android:color/transparent"/>

Espero que isso poupe alguém a algum tempo.


1
Isso está mudando a cor do plano de fundo inteiro e queremos alterar apenas a cor do sublinhado.
shahzain ali

@shahzain ali O sublinhado do EditText é um plano de fundo. Se foi, se você configurá-lo como nulo. E é por isso que backgroundTint vai trabalhados (mas não vai mudar de cor quando ativado)
nyconing

7

Para mim, modifiquei o AppTheme e um valor colors.xml Tanto o colorControlNormal quanto o colorAccent me ajudaram a alterar a cor da borda EditText. Assim como o cursor e o "|" quando dentro de um EditText.

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorControlNormal">@color/yellow</item>
    <item name="colorAccent">@color/yellow</item>
</style>

Aqui está o colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="yellow">#B7EC2A</color>
</resources>

Tirei o atributo android: textCursorDrawable para @ null que coloquei dentro do estilo editText. Quando tentei usar isso, as cores não mudaram.


2
Esta deve ser a resposta #
Mina Samy

Funcionou como um encanto. Obrigado
Däñish Shärmà

6

Você pode definir o segundo plano do edittext como um retângulo com menos preenchimento à esquerda, direita e superior para conseguir isso. Aqui está o exemplo xml:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:top="-1dp"
        android:left="-1dp"
        android:right="-1dp"
        android:bottom="1dp"
        >
        <shape android:shape="rectangle">
            <stroke android:width="1dp" android:color="#6A9A3A"/>
        </shape>
    </item>
</layer-list>

Substitua a forma por um seletor se desejar fornecer largura e cor diferentes para o edittext focado.


5

No Activit.XML, adicione o código

<EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:ems="10"
        android:id="@+id/editText"
        android:hint="Informe o usuário"
        android:backgroundTint="@android:color/transparent"/>

Onde BackgroundTint=colora cor desejada


5

Eu uso esse método para alterar a cor da linha com PorterDuff, sem nenhum outro drawable.

public void changeBottomColorSearchView(int color) {
    int searchPlateId = mSearchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null);
    View searchPlate = mSearchView.findViewById(searchPlateId);
    searchPlate.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}

Existe um identificador semelhante para acessar a linha inferior de um EditText simples?
Rémi P

4

Se você deseja alterar os resultados sem usar as cores do aplicativo, use estas linhas no seu tema:

<item name="android:editTextStyle">@android:style/Widget.EditText</item>
<item name="editTextStyle">@android:style/Widget.EditText</item>

Não conheço outra solução.


4

Eu elaborei uma solução funcional para esse problema após 2 dias de luta, a solução abaixo é perfeita para aqueles que desejam alterar apenas alguns textos de edição, alterar / alternar cores através do código java e querem superar os problemas de comportamento diferente nas versões do sistema operacional devido ao uso do método setColorFilter ().

    import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatDrawableManager;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import com.newco.cooltv.R;

public class RqubeErrorEditText extends AppCompatEditText {

  private int errorUnderlineColor;
  private boolean isErrorStateEnabled;
  private boolean mHasReconstructedEditTextBackground;

  public RqubeErrorEditText(Context context) {
    super(context);
    initColors();
  }

  public RqubeErrorEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    initColors();
  }

  public RqubeErrorEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initColors();
  }

  private void initColors() {
    errorUnderlineColor = R.color.et_error_color_rule;

  }

  public void setErrorColor() {
    ensureBackgroundDrawableStateWorkaround();
    getBackground().setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter(
        ContextCompat.getColor(getContext(), errorUnderlineColor), PorterDuff.Mode.SRC_IN));
  }

  private void ensureBackgroundDrawableStateWorkaround() {
    final Drawable bg = getBackground();
    if (bg == null) {
      return;
    }
    if (!mHasReconstructedEditTextBackground) {
      // This is gross. There is an issue in the platform which affects container Drawables
      // where the first drawable retrieved from resources will propogate any changes
      // (like color filter) to all instances from the cache. We'll try to workaround it...
      final Drawable newBg = bg.getConstantState().newDrawable();
      //if (bg instanceof DrawableContainer) {
      //  // If we have a Drawable container, we can try and set it's constant state via
      //  // reflection from the new Drawable
      //  mHasReconstructedEditTextBackground =
      //      DrawableUtils.setContainerConstantState(
      //          (DrawableContainer) bg, newBg.getConstantState());
      //}
      if (!mHasReconstructedEditTextBackground) {
        // If we reach here then we just need to set a brand new instance of the Drawable
        // as the background. This has the unfortunate side-effect of wiping out any
        // user set padding, but I'd hope that use of custom padding on an EditText
        // is limited.
        setBackgroundDrawable(newBg);
        mHasReconstructedEditTextBackground = true;
      }
    }
  }

  public boolean isErrorStateEnabled() {
    return isErrorStateEnabled;
  }

  public void setErrorState(boolean isErrorStateEnabled) {
    this.isErrorStateEnabled = isErrorStateEnabled;
    if (isErrorStateEnabled) {
      setErrorColor();
      invalidate();
    } else {
      getBackground().mutate().clearColorFilter();
      invalidate();
    }
  }
}

Usa em xml

<com.rqube.ui.widget.RqubeErrorEditText
            android:id="@+id/f_signup_et_referral_code"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_toEndOf="@+id/referral_iv"
            android:layout_toRightOf="@+id/referral_iv"
            android:ems="10"
            android:hint="@string/lbl_referral_code"
            android:imeOptions="actionNext"
            android:inputType="textEmailAddress"
            android:textSize="@dimen/text_size_sp_16"
            android:theme="@style/EditTextStyle"/>

Adicionar linhas com estilo

<style name="EditTextStyle" parent="android:Widget.EditText">
    <item name="android:textColor">@color/txt_color_change</item>
    <item name="android:textColorHint">@color/et_default_color_text</item>
    <item name="colorControlNormal">@color/et_default_color_rule</item>
    <item name="colorControlActivated">@color/et_engagged_color_rule</item>
  </style>

código java para alternar cor

myRqubeEditText.setErrorState(true);
myRqubeEditText.setErrorState(false);

2

Fiquei absolutamente perplexo com esse problema. Eu havia tentado de tudo nesse segmento e em outros, mas não importa o que fiz, não consegui mudar a cor do sublinhado para nada além do azul padrão.

Eu finalmente descobri o que estava acontecendo. Eu estava (incorretamente) usando android.widget.EditTextao criar uma nova instância (mas o restante dos meus componentes era da biblioteca appcompat). Eu deveria ter usado android.support.v7.widget.AppCompatEditText. Troquei new EditText(this)com new AppCompatEditText(this) e o problema foi imediatamente resolvido. Acontece que, se você estiver realmente usando AppCompatEditText, ele respeitará apenas o accentColortema (como mencionado em vários comentários acima) e nenhuma configuração adicional será necessária.


2

Este é o mais fácil e mais eficiente / reutilizável / funciona em todas as APIs.
Crie uma classe EditText personalizada da seguinte forma:

public class EditText extends android.widget.EditText {
    public EditText(Context context) {
        super(context);
        init();
    }

    public EditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public EditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        getBackground().mutate().setColorFilter(ContextCompat.getColor(getContext(), R.color.colorAccent), PorterDuff.Mode.SRC_ATOP);
    }
}

Em seguida, use-o assim:

 <company.com.app.EditText
        android:layout_width="200dp"
        android:layout_height="wrap_content"/>

2

Para alterar o plano de fundo EditText dinamicamente, você pode usar ColorStateList .

int[][] states = new int[][] {
    new int[] { android.R.attr.state_enabled}, // enabled
    new int[] {-android.R.attr.state_enabled}, // disabled
    new int[] {-android.R.attr.state_checked}, // unchecked
    new int[] { android.R.attr.state_pressed}  // pressed
};

int[] colors = new int[] {
    Color.BLACK,
    Color.RED,
    Color.GREEN,
    Color.BLUE
};

ColorStateList colorStateList = new ColorStateList(states, colors);

Créditos: Esta resposta do SO sobre o ColorStateList é incrível .


E qual método usar? setBackgroundTintList? Ela exige API 21.
CoolMind


0

Adicione app:backgroundTintabaixo do nível 21. da API, caso contrário, useandroid:backgroundTint .

Para abaixo do nível 21 da API.

<EditText
     android:id="@+id/edt_name"
     android:layout_width="300dp"
     android:layout_height="wrap_content"
     android:textColor="#0012ff"
     app:backgroundTint="#0012ff"/>

Para um nível superior à API 21.

<EditText
     android:id="@+id/edt_name"
     android:layout_width="300dp"
     android:layout_height="wrap_content"
     android:textColor="#0012ff"
     android:backgroundTint="#0012ff"/>

-2

Modifique este método de acordo com sua necessidade. Isso funcionou para mim!

  private boolean validateMobilenumber() {
        if (mobilenumber.getText().toString().trim().isEmpty() || mobilenumber.getText().toString().length() < 10) {
            input_layout_mobilenumber.setErrorEnabled(true);
            input_layout_mobilenumber.setError(getString(R.string.err_msg_mobilenumber));
           // requestFocus(mobilenumber);
            return false;
        } else {
            input_layout_mobilenumber.setError(null);
            input_layout_mobilenumber.setErrorEnabled(false);
            mobilenumber.setBackground(mobilenumber.getBackground().getConstantState().newDrawable());
        }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.