Thay đổi màu nền của chế độ xem trên Android


335

Làm thế nào để bạn tạo hiệu ứng thay đổi màu nền của chế độ xem trên Android?

Ví dụ:

Tôi có một cái nhìn với một màu nền đỏ. Màu nền của khung nhìn chuyển sang màu xanh. Làm thế nào tôi có thể thực hiện chuyển đổi suôn sẻ giữa các màu?

Nếu điều này không thể được thực hiện với lượt xem, một sự thay thế sẽ được hoan nghênh.


11
Mọi người sẽ mong đợi rằng sự chuyển đổi suôn sẻ giữa các màu sắc không phải là vấn đề đối với một nền tảng như Android. Có lẽ các khung nhìn không được xây dựng cho nó, mặc dù. Câu hỏi vẫn còn.
hpique

Câu trả lời:


374

Tôi cuối cùng đã tìm ra một giải pháp (khá tốt) cho vấn đề này!

Bạn có thể sử dụng TransitionDrawable để thực hiện điều này. Ví dụ: trong một tệp XML trong thư mục có thể vẽ, bạn có thể viết một cái gì đó như:

<?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>

Sau đó, trong XML của bạn cho Chế độ xem thực tế, bạn sẽ tham chiếu TransitionDrawable này trong android:backgroundthuộc tính.

Tại thời điểm này, bạn có thể bắt đầu chuyển đổi trong mã lệnh của mình bằng cách thực hiện:

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

Hoặc chạy quá trình chuyển đổi ngược lại bằng cách gọi:

transition.reverseTransition(transitionTime);

Xem câu trả lời của Roman cho một giải pháp khác bằng API Hoạt hình Thuộc tính, không có sẵn tại thời điểm câu trả lời này ban đầu được đăng.


3
Cảm ơn mxrider! Điều này không trả lời câu hỏi. Thật không may, nó không hoạt động với tôi vì chế độ xem có TransitionDrawable làm nền cũng phải được làm động, và có vẻ như hoạt hình xem ghi đè chuyển đổi nền. Có ý kiến ​​gì không?
hpique

6
Đã giải quyết nó bằng cách bắt đầu TransitionDrawable SAU khi bắt đầu hoạt hình.
hpique

2
Tuyệt vời! Tôi vui vì bạn đã làm nó hoạt động! Có rất nhiều thứ hay ho bạn có thể làm trong xml với Android - phần lớn trong số đó bị chôn vùi trong các tài liệu và theo quan điểm của tôi thì không rõ ràng.
thần tượng hóa

4
Không rõ ràng chút nào. Tôi nên đề cập rằng tôi không sử dụng xml trong trường hợp của mình, vì màu sắc là động. Giải pháp của bạn cũng hoạt động nếu bạn tạo TransitionDrawable bằng mã.
hpique

2
Một trong những nhược điểm chính là không có trình nghe hoàn chỉnh hoạt hình
Leo Droidcoder

508

Bạn có thể sử dụng Api hoạt hình mới cho hoạt hình màu:

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();

Để tương thích ngược với Android 2.x, hãy sử dụng thư viện Nine Old Androids từ Jake Wharton.

Các getColorphương pháp được tán thành trong Android M, vì vậy bạn có hai lựa chọn:

  • Nếu bạn sử dụng thư viện hỗ trợ, bạn cần thay thế các getColorcuộc gọi bằng:

    ContextCompat.getColor(this, R.color.red);
  • nếu bạn không sử dụng thư viện hỗ trợ, bạn cần thay thế các getColorcuộc gọi bằng:

    getColor(R.color.red);

2
Giải pháp "musthave" nếu bạn đang sử dụng API hoạt hình thuộc tính (hoặc NineOldAndroid). Hoạt hình rất mượt mà.
Dmitry Zaytsev

1
Có cách nào để thiết lập thời lượng của quá trình chuyển đổi theo cách này không?
iGio90

159
@ iGio90 có, bạn sẽ không tin nhưng phương thức gọi setDuration () :)
Roman Minenok

6
ValueAnimator.ofArgb(colorFrom, colorTo)nhìn nhiều hơn, nhưng đáng buồn là nó mới.
TWiStErRob

1
Giải pháp đẹp! Cảm ơn bạn.
Steve Folly

137

Tùy thuộc vào cách xem của bạn có được màu nền của nó và cách bạn có được màu mục tiêu của mình, có một số cách khác nhau để làm điều này.

Hai cái đầu tiên sử dụng Hoạt hình thuộc tính Android khung .

Sử dụng Object Animator nếu:

  • Chế độ xem của bạn có màu nền được xác định là argb giá trị trong tệp xml.
  • Chế độ xem của bạn trước đây đã được đặt màu bởi view.setBackgroundColor()
  • Chế độ xem của bạn có màu nền được xác định trong dạng có thể vẽ mà KHÔNG xác định bất kỳ thuộc tính bổ sung nào như bán kính góc hoặc bán kính góc.
  • Chế độ xem của bạn có màu nền được xác định trong dạng có thể vẽ và bạn muốn xóa bất kỳ thuộc tính bổ sung nào như đột quỵ hoặc bán kính góc, hãy nhớ rằng việc xóa các thuộc tính bổ sung sẽ không hoạt hình.

Trình hoạt hình đối tượng hoạt động bằng cách gọi view.setBackgroundColorcái nào thay thế cho drawable được xác định trừ khi nó là một thể hiện của a ColorDrawable, điều mà nó hiếm khi xảy ra. Điều này có nghĩa là bất kỳ thuộc tính nền bổ sung nào từ một nét vẽ có thể vẽ như đột quỵ hoặc góc sẽ bị xóa.

Sử dụng một Animator giá trị nếu:

  • Chế độ xem của bạn có màu nền được xác định trong một hình vẽ có thể đặt các thuộc tính như bán kính góc hoặc bán kính VÀ bạn muốn thay đổi nó thành màu mới được quyết định trong khi chạy.

Sử dụng một Transition drawable nếu:

  • Chế độ xem của bạn nên chuyển đổi giữa hai drawable đã được xác định trước khi triển khai.

Tôi đã gặp một số vấn đề về hiệu năng với các bản vẽ Transition chạy trong khi tôi đang mở một Ngăn kéo mà tôi không thể giải quyết, vì vậy nếu bạn gặp phải bất kỳ sự cố nói lắp bất ngờ nào, bạn có thể gặp phải lỗi tương tự như tôi.

Bạn sẽ phải sửa đổi ví dụ Giá trị hoạt hình nếu bạn muốn sử dụng có thể vẽ được StateLists hoặc LayerLists , nếu không nó sẽ bị sập trên final GradientDrawable background = (GradientDrawable) view.getBackground();dòng.

Đối tượng hoạt hình :

Xem định nghĩa:

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

Tạo và sử dụng ObjectAnimatornhư thế này.

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

Bạn cũng có thể tải định nghĩa hoạt hình từ xml bằng cách sử dụng AnimatorInflater giống như XMight trong Android objectAnimator animate backgroundColor of Layout

Nhà hoạt hình giá trị :

Xem định nghĩa:

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

Định nghĩa có thể vẽ:

<?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>

Tạo và sử dụng ValueAnimator như thế này:

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();

Chuyển tiếp có thể rút ra :

Xem định nghĩa:

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

Định nghĩa có thể vẽ:

<?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>

Sử dụng TransitionDrawable như thế này:

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

Bạn có thể đảo ngược các hình ảnh động bằng cách gọi .reverse()vào ví dụ hoạt hình.

Có một số cách khác để làm hoạt hình nhưng ba cách này có lẽ là phổ biến nhất. Tôi thường sử dụng ValueAnimator.


thực tế có thể định nghĩa và sử dụng TransitionDrawable trong mã, nhưng vì một số lý do, nó không hoạt động lần thứ hai. kỳ dị.
nhà phát triển Android

Tôi có thể sử dụng trình hoạt hình đối tượng ngay cả khi chế độ xem của tôi có bộ ColorDrawable. Tôi chỉ lưu nó vào một biến cục bộ, đặt nó thành null và khi hoạt ảnh kết thúc, hãy đặt lại ColorDrawable ban đầu.
Miloš ernilovský

55

Bạn có thể làm một hoạt hình đối tượng. Ví dụ: tôi có targetView và tôi muốn thay đổi màu nền của bạn:

int colorFrom = Color.RED;
int colorTo = Color.GREEN;
int duration = 1000;
ObjectAnimator.ofObject(targetView, "backgroundColor", new ArgbEvaluator(), colorFrom, colorTo)
    .setDuration(duration)
    .start();

2
Từ API 21, bạn có thể sử dụng ofArgb(targetView, "backgroundColor", colorFrom, colorTo). Nó thực hiện chỉ là ofInt(...).setEvaluator(new ArgbEvaluator()).
ypresto

20

Nếu bạn muốn hoạt hình màu như thế này,

Nhập mô tả hình ảnh ở đây

mã này sẽ giúp bạn:

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();

9
Đâu là phần khi anim biến được khởi tạo?
VoW

@VoW, đây chỉ là ví dụ của ValueAnimator.
RBK

Nó không phải là anim.setRepeatCount(ValueAnimator.INFINITE);Animation.INFINITE bây giờ cũng vậy, nhưng chúng tôi không có gì đảm bảo
MikhailKrishtop

@MikhailKrishtop có gì sai với Animation.INFINITE? developer.android.com/reference/android/view/animation/ từ
RBK

14

cách tốt nhất là sử dụng ValueAnimatorColorUtils.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();

12

Một cách dễ dàng khác để đạt được điều này là thực hiện mờ dần bằng AlphaAnimation.

  1. Đặt chế độ xem của bạn thành Viewgroup
  2. Thêm chế độ xem con vào nó ở chỉ mục 0, với kích thước bố cục match_parent
  3. Cho con của bạn cùng một nền với container
  4. Thay đổi nền của thùng chứa sang màu đích
  5. Làm mờ dần đứa trẻ bằng cách sử dụng AlphaAnimation.
  6. Loại bỏ con khi hoạt ảnh hoàn tất (sử dụng AnimationListener)

đó là một mánh khóe! làm tốt . . .
elyar abad

9

Đây là phương pháp tôi sử dụng trong Hoạt động cơ sở để thay đổi nền. Tôi đang sử dụng GradientDrawables được tạo trong mã, nhưng có thể được điều chỉnh cho phù hợp.

    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);
            }
        }
    }

cập nhật: Trong trường hợp bất kỳ ai gặp phải vấn đề tương tự tôi đã tìm thấy, vì một số lý do trên Android <4.3 sử dụng setCrossFadeEnabled(true)gây ra hiệu ứng trắng không mong muốn nên tôi đã phải chuyển sang màu đơn sắc cho <4.3 bằng phương pháp @Roman Minenok ValueAnimator đã lưu ý ở trên.


Điều này thật đúng với gì mà tôi đã tìm kiếm. Cảm ơn! :)
cyph3r

7

Câu trả lời được đưa ra theo nhiều cách. Bạn cũng có thể sử dụng ofArgb(startColor,endColor)của ValueAnimator.

cho 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();

7

Đây là một chức năng hay cho phép điều này:

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();
    }
}

Và trong 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()
    }
}

điều này không hỗ trợ điện thoại phiên bản cấp thấp hơn
Kartheek s

2
@Kartheek bạn có nghĩa là Android 10 trở xuống? nếu vậy, bạn có thể dễ dàng sử dụng thư viện "ninOldAndroids" để hỗ trợ nó: ninoldandroids.com . cú pháp gần như giống hệt nhau.
nhà phát triển Android

đối với phiên bản thấp hơn colorAnimation.addUpdateListener (new ValueAnimator.AnimatorUpdateListener () {@Override công khai void trên AnimationUpdate (hoạt hình ValueAnimator) {colorDrawable.setColor (hoạt hình ValueAnimator) {colorDrawable.setColor ((Integer) hoạt hình). ;
Mahbubur Rahman Khan

6

Các tài liệu về hoạt hình được hỗ trợ XML là khủng khiếp. Tôi đã tìm kiếm hàng giờ chỉ để làm động màu nền của một nút khi nhấn ... Điều đáng buồn là hoạt hình chỉ cách một thuộc tính: Bạn có thể sử dụng exitFadeDurationtrong 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>

Sau đó, chỉ cần sử dụng nó như backgroundcho quan điểm của bạn. Không cần mã Java / Kotlin.


Bạn đã cứu ngày của tôi. Cảm ơn.
Federico Heiland

1
Câu trả lời tuyệt vời. Lưu ý rằng bạn cũng có thể cần android:enterFadeDuration; kinh nghiệm của tôi là, không có nó, hoạt hình của tôi đã không hoạt động lần thứ hai.
Chuỗi

5

thêm một thư mục hoạt hình vào thư mục res . (tên phải là hoạt hình ). Thêm một tệp tài nguyên hoạt hình. Ví dụ: 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>

Bên trong tập tin java Activity, gọi đây

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

3

Sử dụng chức năng dưới đây cho 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()
}

Vượt qua bất cứ cái nhìn nào bạn muốn thay đổi màu sắc.


2

Tôi đã thấy rằng việc triển khai được sử dụng ArgbEvaluatortrong mã nguồn Android thực hiện công việc tốt nhất trong việc chuyển đổi màu sắc. Khi sử dụng HSV, tùy thuộc vào hai màu, quá trình chuyển đổi đã nhảy qua quá nhiều màu sắc đối với tôi. Nhưng phương pháp này thì không.

Nếu bạn đang cố gắng đơn giản làm động, hãy sử dụng ArgbEvaluatorvới ValueAnimatornhư được đề xuất ở đây :

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();

Tuy nhiên, nếu bạn giống tôi và muốn kết nối quá trình chuyển đổi của bạn với một số cử chỉ người dùng hoặc giá trị khác được truyền từ đầu vào, thì điều đó ValueAnimatorkhông giúp ích được gì (trừ khi bạn đang nhắm mục tiêu cho API 22 trở lên, trong trường hợp đó bạn có thể sử dụng ValueAnimator.setCurrentFraction()phương thức ). Khi nhắm mục tiêu bên dưới API 22, hãy bọc mã được tìm thấy trong ArgbEvaluatormã nguồn theo phương pháp của riêng bạn, như được hiển thị bên dưới:

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))));
}

Và sử dụng nó theo cách bạn muốn.


0

Dựa trên câu trả lời của ademar111190 , tôi đã tạo ra phương pháp này theo ý muốn xung màu nền của chế độ xem giữa hai màu bất kỳ:

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();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.