วางเคอร์เซอร์ที่ท้ายข้อความใน EditText


883

ฉันกำลังเปลี่ยนค่าของนั้นบนEditTextkeyListener

EditTextแต่เมื่อฉันเปลี่ยนข้อความเคอร์เซอร์จะย้ายไปจุดเริ่มต้นของ ฉันต้องการเคอร์เซอร์ให้อยู่ท้ายข้อความ

EditTextวิธีการเลื่อนเคอร์เซอร์ไปที่ส่วนท้ายของข้อความในนั้น


3
ฉันเจอปัญหาเดียวกัน แต่สิ่งที่ฉันถามตัวเองดีกว่าคือสาเหตุที่เกิดขึ้นเพื่อที่ฉันจะสามารถแก้ไขปัญหาก่อนแทนที่จะต้องย้ายคาเร็ตด้วยตัวเอง
kaneda

3
@kaneda ฉันเห็นด้วยอย่างสิ้นเชิง แต่มันจะมีประโยชน์ถ้าคุณได้เพิ่มวิธีแก้ปัญหาจริง
AgentKnopf

1
@ Zainodis มันเป็นแค่ความคิด อย่างที่ฉันบอกว่าฉันเจอปัญหาเดียวกันซึ่งไม่ได้แปลว่าฉันได้พบวิธีแก้ปัญหาแล้ว ในกรณีของฉันฉันมีปัญหากับEditTexts ListViewเป็นรายการของ สำหรับการทดลองฉันทำการเปลี่ยนแปลงบางอย่างในListViewซอร์สโค้ดเองซึ่งเป็นสัตว์ที่ค่อนข้างซับซ้อนและทดสอบกับตัวจำลอง มันเกี่ยวข้องกับการจัดการควบคุมโฟกัสโดยส่วนประกอบ แน่นอนว่ามันไม่ใช่ทางออกที่ฉันจะให้เพื่อช่วยเพื่อนของเรา :)
kaneda


วิธีนี้ใช้ไม่ได้กับการOnFocusChangedติดต่อกลับ วิธีแก้ปัญหาคือใส่ setSelection ไว้ใน runnable และรันบน thread หลัก ตรวจสอบที่นี่stackoverflow.com/a/32156989/4514796
Ali Nem

คำตอบ:


1318

ลองสิ่งนี้:

EditText et = (EditText)findViewById(R.id.inbox);
et.setSelection(et.getText().length());

15
สิ่งนี้ใช้ไม่ได้ในกรณีของฉัน ดูเหมือนว่าเมธอด setSelection () จะไม่มีผลใด ๆ มุมมอง EditText ของฉันมี ImageSpans มีวิธีแก้ปัญหาแบบอื่นหรือไม่?
toobsco42

@marqss ฉันมีปัญหาเดียวกันและทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน ฉันใช้ EditText ในกล่องโต้ตอบและเติมข้อความล่วงหน้าจากหน้าจอหลัก เมื่อสิ่งนั้นเกิดขึ้นเคอร์เซอร์ก็ยังคงอยู่ที่จุดเริ่มต้นและไม่ใช่ตอนจบ แต่หลังจากที่ฉันลองทำตามคำแนะนำของคุณทุกอย่างก็ดีตามที่ฉันต้องการ ขอขอบคุณสำหรับการโพสต์นี้.
Vincy

4
เฮ้ @Mullay ตอนนี้ฉันจัดการมันด้วยการเอาชนะonSelectionChanged()วิธีการEditTextเรียน: public void onSelectionChanged(int start, int end) { CharSequence text = getText(); if (text != null) { if (start != text.length() || end != text.length()) { setSelection(text.length(), text.length()); return; } } super.onSelectionChanged(start, end); }
toobsco42

19
คุณอาจต้องใช้ et.post (Runnable ใหม่ ({... et.setSel ... เพื่อเข้าแถว) นี่เป็นเพราะ android รอที่จะทำบางสิ่งเลย์เอาต์จนกว่าจะถึงเวลาที่ดีกว่าโดยการโพสต์ดังนั้นหากคุณพยายาม setSelection ก่อนที่ระบบจะเสร็จสิ้นจะเป็นการยกเลิกการทำงานของคุณ
MinceMan

1
มันทำงานได้ที่นี่ขอบคุณสำหรับคำตอบของคุณ แต่คุณควรทำเช่นนี้: editText.setText (s); editText.setSelection (editText.getText () ความยาว ().); // หลังจาก setText
smileVann

223

มีฟังก์ชั่นที่เรียกว่าผนวกสำหรับ ediitext ซึ่งผนวกค่าสตริงกับค่า edittext ปัจจุบันและวางเคอร์เซอร์ที่ท้ายของค่า คุณสามารถมีค่าสตริงเป็นค่า ediitext ปัจจุบันและเรียกใช้ append ();

myedittext.append("current_this_edittext_string"); 

1
หากคุณมีข้อความใน EditText อยู่แล้วให้เขียนmyedittext.append("");
ymerdrengene

4
@ymerdrengene เพิ่งใช้myedittext.append("");ไม่ได้ผลสำหรับฉัน!
Chintan Shah

4
สิ่งนี้สมบูรณ์แบบเพราะเพียงแค่แทนที่บรรทัดของรหัสที่ฉันมี ใช้ edittextfield.setText และเพียงแค่สลับเพื่อผนวก ง่ายมาก.
theblitz

ขึ้นอยู่กับการใช้งานว่าใช้ได้หรือไม่ สำหรับฉันมันไม่ได้ การใช้Selectionเป็นวิธีที่ถูกต้องในการบรรลุ IMO นั้น
sud007

139

Kotlin :

ตั้งค่าเคอร์เซอร์ไปที่ตำแหน่งเริ่มต้น :

val editText = findViewById(R.id.edittext_id) as EditText
editText.setSelection(0)

ตั้งค่าเคอร์เซอร์ไปที่จุดสิ้นสุดของ EditText :

val editText = findViewById(R.id.edittext_id) as EditText
editText.setSelection(editText.text.length)

ด้านล่างรหัสคือการวางเคอร์เซอร์หลังตัวละครที่สอง:

val editText = findViewById(R.id.edittext_id) as EditText
editText.setSelection(2)

จาวา :

ตั้งค่าเคอร์เซอร์ไปที่ตำแหน่งเริ่มต้น :

 EditText editText = (EditText)findViewById(R.id.edittext_id);
 editText.setSelection(0);

ตั้งค่าเคอร์เซอร์ไปที่จุดสิ้นสุดของ EditText :

EditText editText = (EditText)findViewById(R.id.edittext_id);
editText.setSelection(editText.getText().length());

ด้านล่างรหัสคือการวางเคอร์เซอร์หลังตัวละครที่สอง:

EditText editText = (EditText)findViewById(R.id.edittext_id);
editText.setSelection(2);

1
สามารถแทนที่findViewById(R.id.edittext_id) as EditTextด้วยfindViewById<EditText>(R.id.edittext_id)หรือเพียงหลีกเลี่ยงการคัดเลือกถ้าใช้API 26+
Evin1_

96

หากคุณโทรsetTextมาก่อนและข้อความใหม่ไม่ได้รับการเรียกใช้เฟสเลย์เอาต์setSelectionในการแยกเรียกใช้งานแยกต่างหากView.post(Runnable)(repost จากหัวข้อนี้ )

ดังนั้นสำหรับฉันรหัสนี้ทำงาน:

editText.setText("text");
editText.post(new Runnable() {
         @Override
         public void run() {
             editText.setSelection(editText.getText().length());
         }
});

แก้ไข 05/16/2019: ตอนนี้ฉันกำลังใช้ Kotlin extension สำหรับสิ่งนั้น:

fun EditText.placeCursorToEnd() {
    this.setSelection(this.text.length)
}

และจากนั้น - editText.placeCursorToEnd ()


ขอบคุณมันใช้งานได้จริงสำหรับฉัน ฉันพยายามโทร setSelection แต่มันไม่ทำงาน ในกรณีของฉันฉันกำลังพยายามตั้งค่าคำนำหน้าคงที่เพื่อแก้ไขข้อความและหลังจากตั้งค่าคำนำหน้าฉันต้องตั้งค่าเคอร์เซอร์เป็นส่วนท้ายของคำนำหน้าเพื่อให้ผู้ใช้สามารถป้อนข้อมูลหลังจากคำนำหน้า เนื่องจากฉันทำ setSelection ใน Constructor ของ TextWatcher อาจเป็นสาเหตุว่าทำไมมันไม่ทำงาน แต่โซลูชันของคุณใช้งานได้ดีมาก!
Wand Maker

ฉันใช้วิธีการโพสต์นี้ในวิธี Clickablespan ของ MultiAutoCompleteTextView ของฉันและใช้งานได้ ... ฉันต้องการให้เคอร์เซอร์เลื่อนที่ส่วนท้ายเมื่อไอคอนลบในช่วงข้อความถูกคลิก ... มันทำงานในโทรศัพท์ Android รุ่นล่าสุดทั้งหมด .. แต่ในรุ่นเก่ามันต้องใช้เวลาพอสมควรหลังจากคลิกครั้งแรก ... รายการอื่น ๆ ทั้งหมดของฉันไม่สามารถคลิกได้ ... เงื่อนงำใด ๆ ??
VijayRaj

1
ฉันไม่รู้ว่าทำไม แต่นี่เป็นสิ่งเดียวที่เหมาะกับฉัน! ข้อความแก้ไขของฉันมีผู้ฟังเปลี่ยนโฟกัส / ข้อความและอยู่ใน TextInputLayout หนึ่งในสิ่งเหล่านั้นกำลังสับสน
Daniel Wilson

42

คุณสามารถวางเคอร์เซอร์ที่ท้ายข้อความในEditTextมุมมองดังนี้:

EditText et = (EditText)findViewById(R.id.textview);
int textLength = et.getText().length();
et.setSelection(textLength, textLength);

27
editText.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        editText.setSelection(editText.getText().length());
        return false;
    }
});

21

นี่เป็นอีกวิธีที่เป็นไปได้:

et.append("");

ลองใช้วิธีนี้หากไม่ได้ผล:

et.setSelection(et.getText().length());

12
/**
 * Set cursor to end of text in edittext when user clicks Next on Keyboard.
 */
View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean b) {
        if (b) {
            ((EditText) view).setSelection(((EditText) view).getText().length());
        }
    }
};

mEditFirstName.setOnFocusChangeListener(onFocusChangeListener); 
mEditLastName.setOnFocusChangeListener(onFocusChangeListener);

มันทำงานได้ดีสำหรับฉัน!


12

ในกรณีของฉันฉันสร้างต่อ kotlin ต่อไปนี้ ฟังก์ชั่นอาจเป็นประโยชน์กับใครบางคน

 private fun EditText.focus(){
        requestFocus()
        setSelection(length())
    }

จากนั้นใช้ดังนี้

mEditText.focus()

12

หาก EditText ของคุณไม่ชัดเจน:

editText.setText("");
editText.append("New text");

หรือ

editText.setText(null);
editText.append("New text");


10

ฉันคิดว่านี่สามารถบรรลุสิ่งที่คุณต้องการ

 Editable etext = mSubjectTextEditor.getText();
 Selection.setSelection(etext, etext.length());

หากการสังเกตของฉันถูกต้องสิ่งนี้จะวางตัวอย่างของ android.widget.TextView $ IClipboardDataPasteEventImpl สำหรับแต่ละวิดเจ็ต ฉันลงเอยด้วยพวกมันมากมาย เห็นได้ชัดว่าพวกเขาทำความสะอาดในที่สุด แต่ดูเหมือนจะเพิ่มปริมาณการใช้หน่วยความจำ ฉันมี 12 อินสแตนซ์ของกล่องโต้ตอบเดียวมันเป็นเพียง dominator (อะไรที่ทำให้มันมีชีวิตอยู่) เป็นอินสแตนซ์ที่กล่าวถึงข้างต้น
AgentKnopf

วางส่วนของรหัสนั้นในตำแหน่งที่เหมาะสมในรหัส (ที่คุณกำลังต่อท้ายข้อความโดยตรงกับ EText) ขอบคุณ!
sud007

9

คำถามเก่าและตอบแล้ว แต่ฉันคิดว่ามันอาจมีประโยชน์ที่จะมีคำตอบนี้หากคุณต้องการใช้เครื่องมือ DataBinding ที่เพิ่งเปิดตัวใหม่สำหรับ Android เพียงตั้งค่านี้ใน XML ของคุณ:

<data>
    <variable name="stringValue" type="String"/>
</data>
...
<EditText
        ...
        android:text="@={stringValue}"
        android:selection="@{stringValue.length()}"
        ...
/>

6

ป้อนคำอธิบายรูปภาพที่นี่

สวัสดีลองสิ่งนี้

 <EditText
       android:id="@+id/edt_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="Hello World!"
       android:cursorVisible="true"/>

EditText editText = findViewById(R.id.editText); editText.setSelection(editText.getText().length()); // End pointเคอร์เซอร์


5

หากคุณต้องการเลือกข้อความทั้งหมดและเพียงแค่ป้อนข้อความใหม่แทนที่จะเป็นข้อความเก่าคุณสามารถใช้

    android:selectAllOnFocus="true"


5

editText.setSelectionเป็นเวทมนตร์ที่นี่ การเลือกโดยทั่วไปจะให้คุณวางเคอร์เซอร์ในตำแหน่งที่คุณต้องการ

EditText editText = findViewById(R.id.editText);
editText.setSelection(editText.getText().length());

วางเคอร์เซอร์ไว้ที่จุดสิ้นสุดของ EditText โดยทั่วไปeditText.getText().length()จะให้ความยาวข้อความ จากนั้นคุณใช้setSelectionกับความยาว

editText.setSelection(0);

ใช้สำหรับตั้งค่าเคอร์เซอร์ที่ตำแหน่งเริ่มต้น (0)


4

รหัสอื่น ๆ ทั้งหมดที่ฉันทดสอบไม่ทำงานได้ดีเนื่องจากผู้ใช้ยังคงสามารถวางเครื่องหมายรูปหมวก / เคอร์เซอร์ได้ทุกที่ที่อยู่ตรงกลางของสตริง (เช่น: 12 | 3.00 - โดยที่เคอร์เซอร์อยู่ที่ไหน) โซลูชันของฉันวางเคอร์เซอร์ไว้ที่ส่วนท้ายของสตริงทุกครั้งที่สัมผัสกับ EditText

ทางออกที่ดีที่สุดคือ:

// For a EditText like:
<EditText
                android:id="@+id/EditTextAmount"
                android:layout_height="wrap_content"
                android:layout_width="fill_parent"
                android:hint="@string/amount"
                android:layout_weight="1"
                android:text="@string/zero_value"
                android:inputType="text|numberDecimal"
                android:maxLength="13"/>

@ string / amount = "0.00" @ string / zero_value = "0.00"

// Create a Static boolean flag
private static boolean returnNext; 


// Set caret/cursor to the end on focus change
EditTextAmount.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View editText, boolean hasFocus) {
                if(hasFocus){
                    ((EditText) editText).setSelection(((EditText) editText).getText().length());
                }
            }
        });

// Create a touch listener and put caret to the end (no matter where the user touched in the middle of the string)
EditTextAmount.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View editText, MotionEvent event) {
                ((EditText) editText).onTouchEvent(event);
                ((EditText) editText).setSelection(((EditText) editText).getText().length());
                return true;
            }
        });


// Implement a Currency Mask with addTextChangedListener
EditTextAmount.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String input = s.toString();
                String output = new String();
                String buffer = new String();
                String decimals = new String();
                String numbers = Integer.toString(Integer.parseInt(input.replaceAll("[^0-9]", "")));

                if(returnNext){
                    returnNext = false;
                    return;
                }

                returnNext = true;

                if (numbers.equals("0")){
                    output += "0.00";
                }
                else if (numbers.length() <= 2){
                    output += "0." + String.format("%02d", Integer.parseInt(numbers));
                }
                else if(numbers.length() >= 3){
                    decimals = numbers.substring(numbers.length() - 2);
                    int commaCounter = 0;
                    for(int i=numbers.length()-3; i>=0; i--){
                        if(commaCounter == 3){
                            buffer += ",";
                            commaCounter = 0;
                        }
                        buffer += numbers.charAt(i);
                        commaCounter++;
                    }
                    output = new StringBuilder(buffer).reverse().toString() + "." + decimals;
                }
                EditTextAmount.setText(output);
                EditTextAmount.setSelection(EditTextAmount.getText().length());
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                /*String input = s.toString();
                if(input.equals("0.0")){
                    EditTextAmount.setText("0.00");
                    EditTextAmount.setSelection(EditTextAmount.getText().length());
                    return;
                }*/
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

หวังว่ามันจะช่วย!


3

สำหรับ ViewModel, LiveData และ Data binding

ฉันต้องการฟังก์ชั่นนี้สำหรับEditTextการรองรับหลายบรรทัดในแอพโน้ตของฉัน ฉันต้องการเคอร์เซอร์ที่ท้ายข้อความเมื่อผู้ใช้นำทางไปยังส่วนที่มีข้อความบันทึกย่อ

วิธีการแก้ปัญหาที่แนะนำโดยdjleop เข้ามาใกล้ แต่ปัญหาคือถ้าผู้ใช้วางเคอร์เซอร์ไว้ตรงกลางของข้อความเพื่อทำการแก้ไขและเริ่มพิมพ์เคอร์เซอร์จะข้ามไปที่ส่วนท้ายของข้อความอีกครั้ง สิ่งนี้เกิดขึ้นเพราะLiveDataจะปล่อยค่าใหม่และเคอร์เซอร์จะข้ามไปที่ส่วนท้ายของข้อความอีกครั้งทำให้ผู้ใช้ไม่สามารถแก้ไขข้อความที่อยู่ตรงกลาง

เพื่อแก้ปัญหานี้ฉันใช้MediatorLiveDataและกำหนดความยาวStringเพียงครั้งเดียวโดยใช้ธง สิ่งนี้จะทำให้ LiveData อ่านค่าเพียงครั้งเดียวนั่นคือเมื่อผู้ใช้นำทางไปยังส่วน หลังจากนั้นผู้ใช้สามารถวางเคอร์เซอร์ได้ทุกที่ที่ต้องการแก้ไขข้อความ

ViewModel

private var accessedPosition: Boolean = false

val cursorPosition = MediatorLiveData<Event<Int>>().apply {
    addSource(yourObject) { value ->
        if(!accessedPosition) {
            setValue(Event(yourObject.note.length))
            accessedPosition = true
        }
    }
}

นี่yourObjectคือ LiveData อื่นที่ดึงมาจากฐานข้อมูลที่เก็บข้อความ String EditTextที่คุณแสดงใน

จากนั้นผูกสิ่งนี้MediatorLiveDataกับ EditText ของคุณโดยใช้อะแดปเตอร์ผูกพัน

XML

ใช้การเชื่อมโยงข้อมูลแบบสองทางสำหรับการแสดงข้อความรวมถึงการยอมรับการป้อนข้อความ

<!-- android:text must be placed before cursorPosition otherwise we'll get IndexOutOfBounds exception-->
<EditText
    android:text="@={viewModel.noteText}"
    cursorPosition="@{viewModel.cursorPosition}" />

อะแดปเตอร์ที่มีผลผูกพัน

@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
    event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}

Event ชั้น

Eventระดับที่นี่เป็นเหมือนSingleLiveEventเขียนโดยโฮเซAlcérrecaจาก Google ฉันใช้ที่นี่เพื่อดูแลการหมุนหน้าจอ การใช้ซิงก์Eventจะทำให้แน่ใจว่าเคอร์เซอร์จะไม่ข้ามไปที่ส่วนท้ายของข้อความเมื่อผู้ใช้แก้ไขข้อความตรงกลางและหน้าจอจะหมุน มันจะรักษาตำแหน่งเดิมเมื่อหน้าจอหมุน

นี่คือEventคลาส:

open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

นี่เป็นวิธีแก้ปัญหาที่เหมาะกับฉันและมอบประสบการณ์การใช้งานที่ดี หวังว่ามันจะช่วยในโครงการของคุณด้วย


คำตอบนี้จะต้องมีขึ้น ขอบคุณมาก
Abdul Rahman Shamair

2

คล้ายกับคำตอบของ @Anh Duy แต่มันไม่ได้ผลสำหรับฉัน ฉันยังต้องการเคอร์เซอร์เพื่อย้ายไปยังจุดสิ้นสุดเฉพาะเมื่อผู้ใช้แตะข้อความแก้ไขและยังสามารถเลือกตำแหน่งของเคอร์เซอร์หลังจากนั้นนี่เป็นรหัสเดียวที่ทำงานให้ฉัน

boolean textFocus = false; //define somewhere globally in the class

//in onFinishInflate() or somewhere
editText.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        editText.onTouchEvent(event);

        if(!textFocus) {
            editText.setSelection(editText.getText().length());
            textFocus = true;
        }

        return true;
    }
});

editText.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        textFocus = false;
    }
});



2

คำตอบข้างต้นไม่ได้ผลสำหรับฉัน ดังนั้นฉันจึงพบทางออกใหม่ อาจเป็นประโยชน์กับใครบางคน ฉันใช้ Android Studio เวอร์ชันล่าสุด 3.5 ตามวันที่ปัจจุบัน บางทีนั่นอาจเป็นเหตุผลที่คำตอบข้างต้นไม่ได้แสดงผล

รหัส:

EditText available_seats = findViewById(R.id.available_seats);
Selection.setSelection(available_seats.getText(),available_seats.getText().length());

ที่นี่อาร์กิวเมนต์ที่ 1 คือค่าข้อความ Spannable ที่คุณต้องการเล่นโดยที่อาร์กิวเมนต์ที่ 2 เป็นค่าดัชนี เนื่องจากเราต้องการเคอร์เซอร์ที่ท้ายข้อความเราจึงได้รับ getText (). length () ซึ่งคืนความยาวของข้อความ


1
ด้วยความยินดี! Happy Coding!
Prajwal W

1
public class CustomEditText extends EditText {
    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomEditText(Context context) {
        super(context);
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        this.setSelection(this.getText().length());
    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {

    }
}

ผู้ใช้ CustomEditText นี้ในไฟล์ XML ของคุณซึ่งจะใช้งานได้ ฉันได้ทดสอบสิ่งนี้แล้วและมันก็ใช้ได้ผลสำหรับฉัน


1

หากคุณต้องการวางเคอร์เซอร์ที่ท้ายข้อความในมุมมอง EditText

 EditText rename;
 String title = "title_goes_here";
 int counts = (int) title.length();
 rename.setSelection(counts);
 rename.setText(title);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.