Menghidupkan perubahan warna latar tampilan pada Android

335

Bagaimana Anda menganimasikan perubahan warna latar belakang tampilan di Android?

Sebagai contoh:

Saya memiliki pandangan dengan warna latar belakang merah. Warna latar belakang tampilan berubah menjadi biru. Bagaimana saya bisa melakukan transisi yang mulus antar warna?

Jika ini tidak dapat dilakukan dengan tampilan, sebuah alternatif akan diterima.

hpique
sumber
11
Orang akan berharap bahwa transisi yang mulus antara warna seharusnya tidak menjadi masalah untuk platform seperti Android. Mungkin pandangan tidak dibangun untuk itu. Pertanyaannya masih ada.
hpique
1
Sebuah baik tutorial video
Alex Jolig

Jawaban:

374

Saya akhirnya mencari solusi (cukup bagus) untuk masalah ini!

Anda dapat menggunakan TransitionDrawable untuk mencapai ini. Misalnya, dalam file XML di folder yang dapat digambar Anda dapat menulis sesuatu seperti:

<?xml version="1.0" encoding="UTF-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- The drawables used here can be solid colors, gradients, shapes, images, etc. -->
    <item android:drawable="@drawable/original_state" />
    <item android:drawable="@drawable/new_state" />
</transition>

Kemudian, dalam XML untuk Tampilan aktual Anda akan mereferensikan TransitionDrawable ini dalam android:backgroundatribut.

Pada titik ini Anda dapat memulai transisi pada kode Anda saat diminta dengan melakukan:

TransitionDrawable transition = (TransitionDrawable) viewObj.getBackground();
transition.startTransition(transitionTime);

Atau jalankan transisi secara terbalik dengan menelepon:

transition.reverseTransition(transitionTime);

Lihat jawaban Roman untuk solusi lain menggunakan API Animasi Properti, yang tidak tersedia pada saat jawaban ini awalnya diposting.

mendewakan
sumber
3
Terima kasih mxrider! Ini memang menjawab pertanyaan. Sayangnya itu tidak berfungsi untuk saya karena pandangan yang memiliki TransitionDrawable sebagai latar belakang harus dianimasikan juga, dan tampaknya animasi tampilan mengabaikan transisi latar belakang. Ada ide?
hpique
6
Memecahkannya dengan memulai TransitionDrawable SETELAH memulai animasi.
hpique
2
Luar biasa! Senang Anda berhasil! Ada banyak hal keren yang dapat Anda lakukan dalam xml dengan Android - banyak yang agak terkubur dalam dokumen dan tidak jelas menurut pendapat saya.
mengidolakan
4
Tidak jelas sama sekali. Saya harus menyebutkan bahwa saya tidak menggunakan xml dalam kasus saya, karena warnanya dinamis. Solusi Anda juga berfungsi jika Anda membuat TransitionDrawable by code.
hpique
2
Salah satu kelemahan utama adalah bahwa tidak ada pendengar animasi lengkap
Leo Droidcoder
511

Anda dapat menggunakan Api Animasi Properti baru untuk animasi warna:

int colorFrom = getResources().getColor(R.color.red);
int colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(250); // milliseconds
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        textView.setBackgroundColor((int) animator.getAnimatedValue());
    }

});
colorAnimation.start();

Untuk kompatibilitas dengan Android 2.x gunakan perpustakaan Old Nine Androids dari Jake Wharton.

The getColorMetode ini usang dalam Android M, sehingga Anda memiliki dua pilihan:

  • Jika Anda menggunakan perpustakaan dukungan, Anda perlu mengganti getColorpanggilan dengan:

    ContextCompat.getColor(this, R.color.red);
  • jika Anda tidak menggunakan perpustakaan dukungan, Anda perlu mengganti getColorpanggilan dengan:

    getColor(R.color.red);
Roman Minenok
sumber
2
Solusi "musthave" jika Anda menggunakan API Animasi Properti (atau NineOldAndroid). Animasi sangat lancar.
Dmitry Zaytsev
1
Apakah ada cara untuk mengatur durasi transisi dengan cara ini?
iGio90
159
@ iGio90 ya, Anda tidak akan mempercayainya tetapi panggilan metode setDuration () :)
Roman Minenok
6
ValueAnimator.ofArgb(colorFrom, colorTo)terlihat lebih to the point, tapi sayangnya itu baru.
TWiStErRob
1
Solusi indah! Terima kasih.
Steve Folly
137

Tergantung pada bagaimana tampilan Anda mendapatkan warna latar belakang dan bagaimana Anda mendapatkan warna target Anda ada beberapa cara berbeda untuk melakukan ini.

Dua yang pertama menggunakan kerangka Animasi Properti Android .

Gunakan Obyek Animator jika:

  • Tampilan Anda memiliki warna latar belakang yang ditentukan sebagai argbnilai dalam file xml.
  • Tampilan Anda sebelumnya telah diatur warnanya oleh view.setBackgroundColor()
  • Tampilan Anda memiliki warna latar belakang yang ditentukan dalam gambar yang TIDAK TIDAK mendefinisikan properti tambahan seperti jari atau sudut radio.
  • Tampilan Anda memiliki warna latar belakang yang ditentukan dalam gambar dan Anda ingin menghapus properti tambahan seperti stroke atau sudut radio, perlu diingat bahwa penghapusan properti tambahan tidak akan dianimasikan.

Objek animator bekerja dengan memanggil view.setBackgroundColoryang menggantikan drawable yang didefinisikan kecuali apakah itu instance dari a ColorDrawable, yang jarang ada. Ini berarti bahwa setiap properti latar belakang tambahan dari stroke atau sudut yang dapat digambar akan dihapus.

Gunakan Animator Nilai jika:

  • Tampilan Anda memiliki warna latar belakang yang ditentukan dalam drawable yang juga mengatur properti seperti stroke atau sudut radio DAN Anda ingin mengubahnya menjadi warna baru yang diputuskan saat berjalan.

Gunakan gambar Transisi jika:

  • Tampilan Anda harus beralih di antara dua drawable yang telah ditentukan sebelum penerapan.

Saya telah mengalami beberapa masalah kinerja dengan drawables Transition yang berjalan saat saya membuka DrawerLayout yang belum bisa saya selesaikan, jadi jika Anda menemukan kegagapan yang tidak terduga, Anda mungkin mengalami bug yang sama seperti saya.

Anda harus mengubah contoh Value Animator jika Anda ingin menggunakan drawable StateLists atau LayerLists drawable , jika tidak maka ia akan crash pada final GradientDrawable background = (GradientDrawable) view.getBackground();baris.

Obyek Animator :

Lihat definisi:

<View
    android:background="#FFFF0000"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Buat dan gunakan yang ObjectAnimatorseperti ini.

final ObjectAnimator backgroundColorAnimator = ObjectAnimator.ofObject(view,
                                                                       "backgroundColor",
                                                                       new ArgbEvaluator(),
                                                                       0xFFFFFFFF,
                                                                       0xff78c5f9);
backgroundColorAnimator.setDuration(300);
backgroundColorAnimator.start();

Anda juga dapat memuat definisi animasi dari xml menggunakan AnimatorInflater seperti XMight di objek AndroidAnimator menghidupkan latar belakangWarna Layout

Nilai Animator :

Lihat definisi:

<View
    android:background="@drawable/example"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Definisi yang dapat digambar:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <stroke
        android:color="#edf0f6"
        android:width="1dp"/>
    <corners android:radius="3dp"/>

</shape>

Buat dan gunakan ValueAnimator seperti ini:

final ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(),
                                                           0xFFFFFFFF,
                                                           0xff78c5f9);

final GradientDrawable background = (GradientDrawable) view.getBackground();
currentAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(final ValueAnimator animator) {
        background.setColor((Integer) animator.getAnimatedValue());
    }

});
currentAnimation.setDuration(300);
currentAnimation.start();

Dapat ditarik transisi :

Lihat definisi:

<View
    android:background="@drawable/example"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Definisi yang dapat digambar:

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="#FFFFFF"/>
            <stroke
                android:color="#edf0f6"
                android:width="1dp"/>
            <corners android:radius="3dp"/>
        </shape>
    </item>

    <item>
        <shape>
            <solid android:color="#78c5f9"/>
            <stroke
                android:color="#68aff4"
                android:width="1dp"/>
            <corners android:radius="3dp"/>
        </shape>
    </item>
</transition>

Gunakan TransitionDrawable seperti ini:

final TransitionDrawable background = (TransitionDrawable) view.getBackground();
background.startTransition(300);

Anda dapat membalikkan animasi dengan memanggil .reverse()instance animasi.

Ada beberapa cara lain untuk melakukan animasi tetapi ketiganya mungkin yang paling umum. Saya biasanya menggunakan ValueAnimator.

Mattias
sumber
sebenarnya dimungkinkan untuk mendefinisikan dan menggunakan TransitionDrawable dalam kode, tetapi untuk beberapa alasan, itu tidak berfungsi untuk kedua kalinya. aneh.
pengembang android
Saya dapat menggunakan animator objek bahkan ketika pandangan saya telah mengatur ColorDrawable. Saya hanya menyimpannya ke variabel lokal, mengaturnya ke nol dan ketika animasi selesai, mengatur kembali ColorDrawable asli.
Miloš Černilovský
55

Anda dapat membuat animator objek. Misalnya, saya memiliki targetView dan saya ingin mengubah warna latar belakang Anda:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();
ademar111190
sumber
2
Dari API 21, Anda dapat menggunakan ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Implementasinya adil ofInt(...).setEvaluator(new ArgbEvaluator()).
ypresto
20

Jika Anda ingin animasi warna seperti ini,

Masukkan deskripsi gambar di sini

kode ini akan membantu Anda:

ValueAnimator anim = ValueAnimator.ofFloat(0, 1);   
anim.setDuration(2000);

float[] hsv;
int runColor;
int hue = 0;
hsv = new float[3]; // Transition color
hsv[1] = 1;
hsv[2] = 1;
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

        hsv[0] = 360 * animation.getAnimatedFraction();

        runColor = Color.HSVToColor(hsv);
        yourView.setBackgroundColor(runColor);
    }
});

anim.setRepeatCount(Animation.INFINITE);

anim.start();
RBK
sumber
9
Di mana bagian ketika variabel anim diinisialisasi?
VoW
@VoW, itu hanya contoh dari ValueAnimator.
RBK
Seharusnya anim.setRepeatCount(ValueAnimator.INFINITE);bukan Animasi. INFINITE untuk saat ini sama, tapi kami tidak punya jaminan
MikhailKrishtop
@MikhailKrishtop ada apa dengan Animation.INFINITE? developer.android.com/reference/android/view/animation/…
RBK
14

Cara terbaik adalah dengan menggunakan ValueAnimator danColorUtils.blendARGB

 ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
 valueAnimator.setDuration(325);
 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {

              float fractionAnim = (float) valueAnimator.getAnimatedValue();

              view.setBackgroundColor(ColorUtils.blendARGB(Color.parseColor("#FFFFFF")
                                    , Color.parseColor("#000000")
                                    , fractionAnim));
        }
});
valueAnimator.start();
Rasoul Miri
sumber
12

Cara mudah lain untuk mencapai ini adalah dengan melakukan fade menggunakan AlphaAnimation.

  1. Jadikan tampilan Anda sebagai ViewGroup
  2. Tambahkan tampilan anak di indeks 0, dengan dimensi tata letak match_parent
  3. Berikan anak Anda latar belakang yang sama dengan wadah
  4. Ubah ke latar belakang wadah ke warna target
  5. Fade out the child menggunakan AlphaAnimation.
  6. Hapus anak ketika animasi selesai (menggunakan AnimationListener)
Stephane JAIS
sumber
9

Ini adalah metode yang saya gunakan dalam Aktivitas Dasar untuk mengubah latar belakang. Saya menggunakan GradientDrawables yang dihasilkan dalam kode, tetapi dapat disesuaikan dengan kebutuhan.

    protected void setPageBackground(View root, int type){
        if (root!=null) {
            Drawable currentBG = root.getBackground();
            //add your own logic here to determine the newBG 
            Drawable newBG = Utils.createGradientDrawable(type); 
            if (currentBG==null) {
                if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN){
                    root.setBackgroundDrawable(newBG);
                }else{
                    root.setBackground(newBG);
                }
            }else{
                TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{currentBG, newBG});
                transitionDrawable.setCrossFadeEnabled(true);
                if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN){
                     root.setBackgroundDrawable(transitionDrawable);
                }else{
                    root.setBackground(transitionDrawable);
                }
                transitionDrawable.startTransition(400);
            }
        }
    }

pembaruan: Jika ada yang mengalami masalah yang sama dengan yang saya temukan, untuk beberapa alasan di Android <4.3 menggunakan setCrossFadeEnabled(true)menyebabkan efek white out yang tidak diinginkan jadi saya harus beralih ke warna solid untuk <4.3 menggunakan metode @Roman Minenok ValueAnimator yang disebutkan di atas.

scottyab
sumber
Ini persis apa yang saya cari. Terima kasih! :)
cyph3r
7

Jawaban diberikan dalam banyak cara. Anda juga dapat menggunakan ofArgb(startColor,endColor)dari ValueAnimator.

untuk API> 21:

int cyanColorBg = ContextCompat.getColor(this,R.color.cyan_bg);
int purpleColorBg = ContextCompat.getColor(this,R.color.purple_bg);

ValueAnimator valueAnimator = ValueAnimator.ofArgb(cyanColorBg,purpleColorBg);
        valueAnimator.setDuration(500);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
              @Override
              public void onAnimationUpdate(ValueAnimator valueAnimator) {
                   relativeLayout.setBackgroundColor((Integer)valueAnimator.getAnimatedValue());
              }
        });
        valueAnimator.start();
Palak Darji
sumber
7

Dokumentasi tentang animasi bertenaga XML mengerikan. Saya telah mencari sekitar jam hanya untuk menghidupkan warna latar belakang tombol ketika menekan ... Yang menyedihkan adalah bahwa animasi hanya satu atribut jauhnya: Anda dapat menggunakan exitFadeDurationdi selector:

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:exitFadeDuration="200">

    <item android:state_pressed="true">
        <shape android:tint="#3F51B5" />
    </item>

    <item>
        <shape android:tint="#F44336" />
    </item>

</selector>

Kemudian gunakan saja backgrounduntuk tampilan Anda. Tidak diperlukan kode Java / Kotlin.

Marius
sumber
Kamu menyelamatkan hariku. Terima kasih.
Federico Heiland
1
Jawaban yang sangat bagus. Perhatikan bahwa Anda mungkin juga perlu android:enterFadeDuration; pengalaman saya adalah bahwa, tanpa itu, animasi saya tidak berfungsi untuk kedua kalinya.
Tali
7

Berikut adalah fungsi bagus yang memungkinkan ini:

public static void animateBetweenColors(final @NonNull View viewToAnimateItsBackground, final int colorFrom,
                                        final int colorTo, final int durationInMs) {
    final ColorDrawable colorDrawable = new ColorDrawable(durationInMs > 0 ? colorFrom : colorTo);
    ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable);
    if (durationInMs > 0) {
        final ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
        colorAnimation.addUpdateListener(animator -> {
            colorDrawable.setColor((Integer) animator.getAnimatedValue());
            ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable);
        });
        colorAnimation.setDuration(durationInMs);
        colorAnimation.start();
    }
}

Dan di Kotlin:

@JvmStatic
fun animateBetweenColors(viewToAnimateItsBackground: View, colorFrom: Int, colorTo: Int, durationInMs: Int) {
    val colorDrawable = ColorDrawable(if (durationInMs > 0) colorFrom else colorTo)
    ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable)
    if (durationInMs > 0) {
        val colorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo)
        colorAnimation.addUpdateListener { animator: ValueAnimator ->
            colorDrawable.color = (animator.animatedValue as Int)
            ViewCompat.setBackground(viewToAnimateItsBackground, colorDrawable)
        }
        colorAnimation.duration = durationInMs.toLong()
        colorAnimation.start()
    }
}
pengembang android
sumber
ini tidak mendukung ponsel versi bawah
Kartheek s
2
@Kartheeks maksudmu Android 10 ke bawah? jika demikian, Anda dapat dengan mudah menggunakan pustaka "nineOldAndroids" untuk mendukungnya: nineoldandroids.com . sintaksisnya hampir persis sama.
pengembang android
untuk versi yang lebih rendah colorAnimation.addUpdateListener (ValueAnimator.AnimatorUpdateListener (new new) {@Override public void onAnimationUpdate (animasi ValueAnimator) {colorDrawable.setColor ((Integer) animation.getAnimatedValue ()); ViewCompat.setBackground (viewToAnimate); ;
Mahbubur Rahman Khan
5

tambahkan folder animator ke folder res . (nama harus animator ). Tambahkan file sumber daya animator. Misalnya res / animator / fade.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="backgroundColor"
        android:duration="1000"
        android:valueFrom="#000000"
        android:valueTo="#FFFFFF"
        android:startOffset="0"
        android:repeatCount="-1"
        android:repeatMode="reverse" />
</set>

Di dalam file java Activity, panggil ini

View v = getWindow().getDecorView().findViewById(android.R.id.content);
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.fade);
set.setTarget(v);
set.start();
canbax
sumber
3

Gunakan fungsi di bawah ini untuk Kotlin:

private fun animateColorValue(view: View) {
    val colorAnimation =
        ValueAnimator.ofObject(ArgbEvaluator(), Color.GRAY, Color.CYAN)
    colorAnimation.duration = 500L
    colorAnimation.addUpdateListener { animator -> view.setBackgroundColor(animator.animatedValue as Int) }
    colorAnimation.start()
}

Lewati tampilan apa pun yang Anda ingin ubah warnanya.

Antek
sumber
2

Saya telah menemukan bahwa implementasi yang digunakan oleh ArgbEvaluatordalam kode sumber Android melakukan pekerjaan terbaik dalam transisi warna. Saat menggunakan HSV, tergantung pada dua warna, transisi melompati terlalu banyak warna untuk saya. Tetapi metode ini tidak.

Jika Anda mencoba menghidupkan, gunakan ArgbEvaluatordengan ValueAnimatorseperti yang disarankan di sini :

ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {

    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        view.setBackgroundColor((int) animator.getAnimatedValue());
    }

});
colorAnimation.start();

Namun, jika Anda seperti saya dan ingin mengikat transisi Anda dengan beberapa isyarat pengguna atau nilai lain yang diteruskan dari input, ValueAnimatoritu tidak banyak membantu (kecuali Anda menargetkan untuk API 22 dan di atas, dalam hal ini Anda dapat menggunakan ValueAnimator.setCurrentFraction()metode ini ). Saat menargetkan di bawah API 22, bungkus kode yang ditemukan dalam ArgbEvaluatorkode sumber dalam metode Anda sendiri, seperti yang ditunjukkan di bawah ini:

public static int interpolateColor(float fraction, int startValue, int endValue) {
    int startA = (startValue >> 24) & 0xff;
    int startR = (startValue >> 16) & 0xff;
    int startG = (startValue >> 8) & 0xff;
    int startB = startValue & 0xff;
    int endA = (endValue >> 24) & 0xff;
    int endR = (endValue >> 16) & 0xff;
    int endG = (endValue >> 8) & 0xff;
    int endB = endValue & 0xff;
    return ((startA + (int) (fraction * (endA - startA))) << 24) |
            ((startR + (int) (fraction * (endR - startR))) << 16) |
            ((startG + (int) (fraction * (endG - startG))) << 8) |
            ((startB + (int) (fraction * (endB - startB))));
}

Dan gunakan sesuka Anda.

fahmy
sumber
0

Berdasarkan jawaban ademar111190 , saya telah membuat metode ini yang akan mengirimkan warna latar belakang tampilan antara dua warna:

private void animateBackground(View view, int colorFrom, int colorTo, int duration) {


    ObjectAnimator objectAnimator = ObjectAnimator.ofObject(view, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo);
    objectAnimator.setDuration(duration);
    //objectAnimator.setRepeatCount(Animation.INFINITE);
    objectAnimator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            // Call this method again, but with the two colors switched around.
            animateBackground(view, colorTo, colorFrom, duration);
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    objectAnimator.start();
}
larangan geoengineering
sumber