ตั้งค่ารหัสทรัพยากรที่วาดได้ใน android: src สำหรับ ImageView โดยใช้การผูกข้อมูลใน Android


94

ฉันกำลังพยายามตั้งรหัสทรัพยากรที่วาดได้เป็น android: src ของ ImageView โดยใช้การผูกข้อมูล

นี่คือวัตถุของฉัน:

public class Recipe implements Parcelable {
    public final int imageResource; // resource ID (e.g. R.drawable.some_image)
    public final String title;
    // ...

    public Recipe(int imageResource, String title /* ... */) {
        this.imageResource = imageResource;
        this.title = title;
    }

    // ...
}

นี่คือเค้าโครงของฉัน:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="recipe"
            type="com.example.android.fivewaystocookeggs.Recipe" />
    </data>

    <!-- ... -->

    <ImageView
        android:id="@+id/recipe_image_view"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scaleType="centerCrop"
        android:src="@{recipe.imageResource}" />

    <!-- ... -->

</layout>

และสุดท้ายชั้นกิจกรรม:

// ...

public class RecipeActivity extends AppCompatActivity {

    public static final String RECIPE_PARCELABLE = "recipe_parcelable";
    private Recipe mRecipe;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mRecipe = getIntent().getParcelableExtra(RECIPE_PARCELABLE);
        ActivityRecipeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_recipe);
        binding.setRecipe(mRecipe);
    }

    // ...

}

มันไม่แสดงภาพเลย ผมทำอะไรผิดหรือเปล่า?

BTW ทำงานได้อย่างสมบูรณ์แบบด้วยวิธีมาตรฐาน:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_recipe);

    final ImageView recipeImageView = (ImageView) findViewById(R.id.recipe_image_view);
    recipeImageView.setImageResource(mRecipe.imageResource);

}

คำตอบ:


114

คำตอบ ณ วันที่ 10 พ.ย. 2559

ความคิดเห็นของ Splash ด้านล่างได้เน้นว่าไม่จำเป็นต้องใช้ประเภทคุณสมบัติที่กำหนดเอง (เช่นimageResource) เราสามารถสร้างหลายวิธีได้แทนandroid:src:

public class DataBindingAdapters {

    @BindingAdapter("android:src")
    public static void setImageUri(ImageView view, String imageUri) {
        if (imageUri == null) {
            view.setImageURI(null);
        } else {
            view.setImageURI(Uri.parse(imageUri));
        }
    }

    @BindingAdapter("android:src")
    public static void setImageUri(ImageView view, Uri imageUri) {
        view.setImageURI(imageUri);
    }

    @BindingAdapter("android:src")
    public static void setImageDrawable(ImageView view, Drawable drawable) {
        view.setImageDrawable(drawable);
    }

    @BindingAdapter("android:src")
    public static void setImageResource(ImageView imageView, int resource){
        imageView.setImageResource(resource);
    }
}

คำตอบเก่า

คุณสามารถลองใช้อะแดปเตอร์ได้ตลอดเวลา:

public class DataBindingAdapters {

    @BindingAdapter("imageResource")
    public static void setImageResource(ImageView imageView, int resource){
        imageView.setImageResource(resource);
    }
}

จากนั้นคุณสามารถใช้อะแดปเตอร์ใน xml ของคุณได้เช่นนั้น

<ImageView
    android:id="@+id/recipe_image_view"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:scaleType="centerCrop"
    imageResource="@{recipe.imageResource}" />

โปรดสังเกตว่าชื่อภายใน xml ตรงกับคำอธิบายประกอบ BindingAdapter (imageResource)

ไม่จำเป็นต้องประกาศคลาส DataBindingAdapters ที่ใดโดยเฉพาะกลไก DataBinding จะพบว่ามันไม่สำคัญ (ฉันเชื่อ)


มันทำงานโดยใช้@BindingAdapter. แต่มูลค่าที่ควรจะเป็นandroid:srcไม่ได้:imageResource @BindingAdapter("android:src")ขอขอบคุณ!
Yuriy Seredyuk

5
@YuriySeredyuk Nooooo! ได้โปรด การทำเช่นนี้จะแทนที่การใช้ "android: src" ทุกครั้งภายใน xml ในแอปพลิเคชันทั้งหมดของคุณซึ่งคุณไม่ต้องการทำ เพื่อให้ imageResource ทำงานได้คุณต้องเปลี่ยน xml สำหรับ imageView เหมือนที่ฉันได้ทำไว้ข้างต้นการเชื่อมฐานข้อมูลจะหาสิ่งที่จะวางไว้ที่นั่น
Joe Maher

1
ตกลงฉันเข้าใจความคิด ตอนนี้ใช้<ImageView imageResource="@{recipe.imageResource}" />และ@BindingAdapter("imageResource"). ฉันเพิ่งพลาดimageResource="@{recipe.imageResource}"บางส่วนจากการตัดโค้ดของคุณ :)
Yuriy Seredyuk

1
ไม่จำเป็นต้องเป็นapp:imageResource?
NameSpace

1
"การทำเช่นนั้นจะลบล้างการใช้" android: src "ทุกครั้งภายใน xml ในแอปพลิเคชันทั้งหมดของคุณ" การค้นหาฐานข้อมูลไม่ฉลาดพอที่จะใช้เฉพาะแอตทริบิวต์นั้นกับ ImageView เพราะนั่นคือสิ่งที่กำหนดไว้ในฟังก์ชัน ฉันคิดว่า "android: src" น่าจะดีกว่า .... ลองพิจารณาว่า Android ทำอะไรสำหรับอะแดปเตอร์เชื่อมต่อ ImageView: android.googlesource.com/platform/frameworks/data-binding/+/…
Splash

45

ไม่จำเป็นต้องมีการกำหนดเองBindingAdapterเลย เพียงแค่ใช้

app:imageResource="@{yourResId}"

และมันจะทำงานได้ดี

ตรวจสอบนี้สำหรับวิธีการทำงาน


2
สิ่งนี้ควรมีการโหวตเพิ่มมากขึ้นเนื่องจากเป็นคำตอบที่ดีประมาณปี 2020
JohnnyLambada

แน่นอนคำตอบที่ดีที่สุดและง่ายที่สุด
luckyhandler

นี่เป็นคำตอบที่ดีและเหมาะสมที่สุด ณ ปลายปี 2020
mcy

ฉันกำลังดูImageViewคลาสและทำตามsetImageResourceวิธีการนี้ดูเหมือนว่าในที่สุดจะได้รับการแก้ไขresolveUriและมีการตรวจสอบความถูกต้องไม่เป็นศูนย์ เพื่อที่จะทำงานให้ฉันสงสัยว่าสิ่งที่อาจเกิดขึ้นหากInt Int?เมื่อดำเนินการผูกโดยตัวอย่างเช่นถ้าสิ่งอื่นที่เรียกexecutePendingBindingsแล้วไม่เป็นโมฆะจะมีค่าเริ่มต้นเป็นศูนย์ nullables เป็น null
cutiko

25

กำหนด:

@BindingAdapter({"android:src"})
public static void setImageViewResource(ImageView imageView, int resource) {
    imageView.setImageResource(resource);
}

ใช้:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:scaleType="center"
    android:src="@{viewModel.imageRes, default=@drawable/guide_1}"/>

ฉันจะเพิ่มวิธีการนั้นได้
ที่ไหน

สนับสนุนเพิ่มในคลาสใด ๆ คุณอาจสร้าง ImageDataBindingAdapter.class
qinmiao

12

อย่าลบล้างแอตทริบิวต์ SDK มาตรฐานเมื่อคุณสร้างของคุณเอง@BindingAdapter!

นี่ไม่ใช่แนวทางที่ดีด้วยเหตุผลหลายประการเช่นจะป้องกันไม่ให้ได้รับประโยชน์จากการแก้ไขใหม่ในการอัปเดต Android SDK ของแอตทริบิวต์นั้น นอกจากนี้ยังอาจสร้างความสับสนให้กับนักพัฒนาและเป็นเรื่องยากสำหรับการนำกลับมาใช้ใหม่ (เนื่องจากไม่ถูกลบล้างว่าจะถูกลบล้าง)

คุณอาจใช้เนมสเปซอื่นเช่น:

custom:src="@{recipe.imageResource}"

หรือ

mybind:src="@{recipe.imageResource}"

------ เริ่มอัปเดต 2. ก.ค. 2018

ไม่แนะนำให้ใช้เนมสเปซดังนั้นควรใช้คำนำหน้าหรือชื่ออื่นเป็น:

app:custom_src="@{recipe.imageResource}"

หรือ

app:customSrc="@{recipe.imageResource}"

------ สิ้นสุดการอัปเดต 2. ก.ค. 2018

อย่างไรก็ตามฉันขอแนะนำวิธีแก้ปัญหาที่แตกต่างกันดังนี้:

android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"

มุมมองบริบทพร้อมใช้งานเสมอในนิพจน์การผูก @{ ... }


1
ฉันคิดว่าควรหลีกเลี่ยงโค้ดภายใน xml ให้มากที่สุด - ไม่สามารถทดสอบได้สามารถซ้อนทับได้และไม่ชัดเจน แต่ฉันยอมรับว่าแอตทริบิวต์มาตรฐานที่มากเกินไปอาจทำให้เกิดความสับสน ฉันคิดว่าวิธีที่ดีที่สุดคือตั้งชื่อแอตทริบิวต์ใหม่ให้แตกต่างกันในกรณีนี้คือ "srcResId" แต่ยังคงใช้ BindingAdapter
Kirill Starostin

8

จากคำตอบจาก Maher Abuthraa นี่คือสิ่งที่ฉันใช้ใน XML:

android:src="@{context.getDrawable(recipe.imageResource)}"

contextตัวแปรสามารถใช้ได้ในการแสดงออกโดยไม่ต้องมีผลผูกพันใด ๆ ที่นำเข้า นอกจากนี้ไม่BindingAdapterจำเป็นต้องกำหนดเอง มีข้อแม้เท่านั้น: วิธีgetDrawableนี้ใช้ได้ตั้งแต่ API 21 เท่านั้น


6

สำหรับKotlinวางสิ่งนี้ไว้ในไฟล์ utils ระดับบนสุดไม่จำเป็นต้องมีบริบทคงที่ / สหาย:

@BindingAdapter("android:src")
fun setImageViewResource(view: ImageView, resId : Int) {
    view.setImageResource(resId)
}

5

ยิ่งทำได้มากขึ้นด้วย DataBindingAdapter

  • คุณสามารถตั้งค่าImage Url , File , Bitmap , Byte Array , Drawable , Drawable Idอะไรก็ได้โดยการผูกข้อมูล
  • คุณสามารถตั้งค่า Error ภาพ / ยึดตำแหน่งรูปภาพเกินไปกับการส่งผ่านหลายพารามิเตอร์อะแดปเตอร์ที่มีผลผูกพัน

ตั้งค่าประเภทใดก็ได้เหล่านี้:

android:src="@{model.profileImage}"

android:src="@{roundIcon ? @drawable/ic_launcher_round : @drawable/ic_launcher_round}"

android:src="@{bitmap}"

android:src="@{model.drawableId}"

android:src="@{@drawable/ic_launcher}"

android:src="@{file}"

android:src="@{`https://placekitten.com/200/200`}"

ตั้งค่าภาพผิดพลาด / ภาพตัวยึด

placeholderImage="@{@drawable/img_placeholder}"
errorImage="@{@drawable/img_error}"


<ImageView
    placeholderImage="@{@drawable/ic_launcher}"
    errorImage="@{@drawable/ic_launcher}"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:src="@{`https://placekitten.com/2000/2000`}"
    />

ทดสอบทุกประเภทแล้ว

วท

ดังนั้นจึงเป็นไปได้ด้วยอะแดปเตอร์การผูกเดียว เพียงแค่คัดลอกโครงการวิธีนี้

public class BindingAdapters {
    @BindingAdapter(value = {"android:src", "placeholderImage", "errorImage"}, requireAll = false)
    public static void loadImageWithGlide(ImageView imageView, Object obj, Object placeholder, Object errorImage) {
        RequestOptions options = new RequestOptions();
        if (placeholder instanceof Drawable) options.placeholder((Drawable) placeholder);
        if (placeholder instanceof Integer) options.placeholder((Integer) placeholder);

        if (errorImage instanceof Drawable) options.error((Drawable) errorImage);
        if (errorImage instanceof Integer) options.error((Integer) errorImage);

        RequestManager manager = Glide.with(App.getInstance()).
                applyDefaultRequestOptions(options);
        RequestBuilder<Drawable> builder;

        if (obj instanceof String) {
            builder = manager.load((String) obj);
        } else if (obj instanceof Uri)
            builder = manager.load((Uri) obj);
        else if (obj instanceof Drawable)
            builder = manager.load((Drawable) obj);
        else if (obj instanceof Bitmap)
            builder = manager.load((Bitmap) obj);
        else if (obj instanceof Integer)
            builder = manager.load((Integer) obj);
        else if (obj instanceof File)
            builder = manager.load((File) obj);
        else if (obj instanceof Byte[])
            builder = manager.load((Byte[]) obj);
        else builder = manager.load(obj);
        builder.into(imageView);
    }
}

เหตุผลที่ฉันใช้ Glide เพื่อโหลดวัตถุทั้งหมด

ถ้าคุณถามฉันว่าทำไมฉันถึงใช้ Glide เพื่อโหลด drawable / resource id ฉันสามารถใช้imageView.setImageBitmap();หรือimageView.setImageResource();. ดังนั้นเหตุผลก็คือ

  • Glide เป็นเฟรมเวิร์กการโหลดรูปภาพที่มีประสิทธิภาพซึ่งครอบคลุมการถอดรหัสสื่อหน่วยความจำและการแคชดิสก์ ดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับรูปภาพและแคชขนาดใหญ่
  • เพื่อให้เกิดความสม่ำเสมอในขณะโหลดภาพ ตอนนี้ทรัพยากรรูปภาพทุกประเภทโหลดโดย Glide

หากคุณใช้ Piccaso, Fresso หรือไลบรารีการโหลดรูปภาพอื่น ๆ คุณสามารถเปลี่ยนแปลงloadImageWithGlideวิธีการได้


`errorImage =" @ {@ drawable / ic_launcher} "". สิ่งนี้ไม่ได้รวบรวมสำหรับฉัน
Vivek Mishra

@VivekMishra บางที ic_launcher ของคุณอยู่ใน mipmap? ลอง @ mipmap / ic_launcher
เขมราช

@VivekMishra คุณวางบันทึกข้อผิดพลาดที่เกี่ยวข้องได้ไหม คุณเพิ่มเมธอดนี้ในคลาสการผูกของคุณหรือไม่?
เขมราช

**** / ข้อผิดพลาดในการผูกข้อมูล **** msg: ไม่พบ getter สำหรับแอตทริบิวต์ 'app: src' ที่มีค่าประเภท java.lang.String บน com.zuowei.circleimageview.CircleImageView ฉันลองใช้ทั้งแอนดรอยด์และเนมสเปซของแอปแล้วทั้งสองอย่างก็ไม่ได้ผลสำหรับฉัน ฉันได้แทนที่ imageview เริ่มต้นด้วย circleImageView ในพารามิเตอร์
Vivek Mishra

นอกจากนี้ฉันได้สร้าง Binding Adapter ในคลาสแยกต่างหาก
Vivek Mishra

3
public Drawable getImageRes() {
        return mContext.getResources().getDrawable(R.drawable.icon);
    }

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:scaleType="center"
    android:src="@{viewModel.imageRes}"/>


2

ฉันไม่ใช่ผู้เชี่ยวชาญด้าน Android แต่ฉันใช้เวลาหลายชั่วโมงในการพยายามถอดรหัสโซลูชันที่มีอยู่ สิ่งที่ดีคือฉันเข้าใจแนวคิดทั้งหมดของการเชื่อมโยงข้อมูลโดยใช้BindingAdapterดีกว่าเล็กน้อย สำหรับสิ่งนั้นอย่างน้อยฉันก็ขอบคุณสำหรับคำตอบที่มีอยู่ (แม้ว่าจะไม่สมบูรณ์มากก็ตาม) นี่คือรายละเอียดทั้งหมดของแนวทาง:

ฉันจะใช้BindingAdapterในตัวอย่างนี้ด้วย การเตรียมxml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="model"
            type="blahblah.SomeViewModel"/>
    </data>

    <!-- blah blah -->

    <ImageView
        android:id="@+id/ImageView"
        app:appIconDrawable="@{model.packageName}"/>

    <!-- blah blah -->
</layout>

ดังนั้นฉันจึงเก็บเฉพาะสิ่งที่สำคัญ:

  • SomeViewModel คือ ViewModelฉันใช้สำหรับการผูกข้อมูล นอกจากนี้คุณยังสามารถใช้คลาสที่ขยายและการใช้งานBaseObservable @Bindableอย่างไรก็ตามBindingAdapterในตัวอย่างนี้ไม่จำเป็นต้องอยู่ในคลาสViewModelหรือBaseObservableคลาส! ชั้นธรรมดาจะทำ! ซึ่งจะแสดงในภายหลัง
  • app:appIconDrawable="@{model.packageName}". ใช่ ... นี่มันทำให้ฉันปวดหัวจริงๆ! มาทำลายมัน:
    • app:appIconDrawable: นี่เป็นอะไรก็ได้: app:iCanBeAnything! จริงๆ. คุณยังสามารถเก็บ"android:src"! อย่างไรก็ตามโปรดทราบว่าคุณเลือกแล้วเราจะใช้ในภายหลัง!
    • "@ {model.packageName}": หากคุณทำงานกับการผูกข้อมูลสิ่งนี้เป็นสิ่งที่คุ้นเคย ฉันจะแสดงวิธีใช้ในภายหลัง

สมมติว่าเราใช้คลาสสังเกตง่ายๆนี้:

public class SomeViewModel extends BaseObservable {
   private String packageName; // this is what @{model.packageName}
                               // access via the getPackageName() !!!
                               // Of course this needs to be set at some
                               // point in your program, before it makes
                               // sense to use it in the BindingAdapter.

   @Bindable
   public String getPackageName() {
       return packageName;
   }

   public void setPackageName(String packageName) {
       this.packageName = packageName;
       notifyPropertyChanged(BR.packageName);
   }

   // The "appIconDrawable" is what we defined above! 
   // Remember, they have to align!! As we said, we can choose whatever "app:WHATEVER".
   // The BindingAdapter and the xml need to be aligned, that's it! :)
   //
   // The name of the function, i.e. setImageViewDrawable, can also be 
   // whatever we want! Doesn't matter.
   @BindingAdapter({"appIconDrawable"})
   public static void setImageViewDrawable(ImageView imageView, String packageName) {
       imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName));
   }
}

ตามสัญญาคุณยังสามารถย้ายไฟล์ public static void setImageViewDrawable()คลาสไปยังคลาสอื่นได้เช่นคุณอาจมีคลาสที่มีคอลเล็กชันBindingAdapters:

public class BindingAdapterCollection {
   @BindingAdapter({"appIconDrawable"})
   public static void setImageViewDrawable(ImageView imageView, String packageName) {
       imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName));
   }
}

ข้อสังเกตที่สำคัญอีกประการหนึ่งคือในObservableชั้นเรียนของฉันฉันเคยString packageNameส่งข้อมูลเพิ่มเติมไปยังไฟล์setImageViewDrawable. คุณยังสามารถเลือกตัวอย่างเช่นint resourceIdด้วย getters / setters ที่เกี่ยวข้องซึ่งอะแด็ปเตอร์จะกลายเป็น:

public class SomeViewModel extends BaseObservable {
   private String packageName; // this is what @{model.packageName}
                               // access via the getPackageName() !!!
   private int resourceId;     // if you use this, don't forget to update
                               // your xml with: @{model.resourceId}

   @Bindable
   public String getPackageName() {
       return packageName;
   }

   public void setPackageName(String packageName) {
       this.packageName = packageName;
       notifyPropertyChanged(BR.packageName);
   }

   @Bindable
   public int getResourceId() {
       return packageName;
   }

   public void setResourceId(int resourceId) {
       this.resourceId = resourceId;
       notifyPropertyChanged(BR.resourceId);
   }

   // For this you use: app:appIconDrawable="@{model.packageName}" (passes String)
   @BindingAdapter({"appIconDrawable"})
   public static void setImageViewDrawable(ImageView imageView, String packageName) {
       imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName));
   }

   // for this you use: app:appIconResourceId="@{model.resourceId}" (passes int)
   @BindingAdapter({"appIconResourceId"})
   public static void setImageViewResourceId(ImageView imageView, int resource) {
       imageView.setImageResource(resource);
   }
}

2

งานนี้สำหรับฉัน ฉันจะเพิ่มในคำตอบ @hqzxzwb เป็นความคิดเห็น แต่เนื่องจากข้อ จำกัด ด้านชื่อเสียง

ฉันมีสิ่งนี้ในมุมมองของฉัน Model

var passport = R.drawable.passport

จากนั้นใน xml ของฉันฉันมี

android:src="@{context.getDrawable(model.passort)}"

และนั่นก็คือ


ตกลง แต่คุณลืมระบุว่าคุณต้องนำเข้าบริบท คุณช่วยอัปเดตคำตอบของคุณได้ไหม
deadfish

1

ใช้ Fresco (ไลบรารีรูปภาพ Facebook)

 public class YourCustomBindingAdapters {

    //app:imageUrl="@{data.imgUri}"
    @BindingAdapter("bind:imageUrl")
    public static void loadImage(SimpleDraweeView imageView, String url) {
        if (url == null) {
            imageView.setImageURI(Uri.EMPTY);
        } else {
            if (url.length() == 0)
                imageView.setImageURI(Uri.EMPTY);
            else
                imageView.setImageURI(Uri.parse(url));
        }
    }
}

0

ในสถานะมุมมองของคุณหรือดูคลาสโมเดล

 fun getSource(context: Context): Drawable? {
        return ContextCompat.getDrawable(context, R.drawable.your_source)
    }

ใน XML ของคุณ

<androidx.appcompat.widget.AppCompatImageButton
   .
   .
   .
   android:src="@{viewState.getSource(context)}"

0
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
           name="model"
           type="YourViewModel"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:background="?android:attr/selectableItemBackground"
          android:paddingStart="@dimen/dp16"
          android:paddingTop="@dimen/dp8"
          android:paddingEnd="@dimen/dp8"
          android:paddingBottom="@dimen/dp8">

          <ImageView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content" 
              android:src="@{model.selected ? @drawable/check_fill : @drawable/check_empty}"/>

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

0

ตั้งภาพเช่นนี้

  <ImageView
        android:layout_width="28dp"
        android:layout_height="28dp"
        android:src="@{model.isActive ? @drawable/white_activated_icon :@drawable/activated_icon}"
        tools:src="@mipmap/white_activated_icon" />
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.