จะลบโฟกัสโดยไม่ตั้งโฟกัสไปที่ตัวควบคุมอื่นได้อย่างไร?


101

ฉันชอบให้ UI ของฉันใช้งานง่าย แต่ละหน้าจอควรจะนำทางผู้ใช้ไปยังขั้นตอนต่อไปในแอพอย่างเป็นธรรมชาติและสงบเสงี่ยม นอกจากนั้นฉันพยายามทำให้สิ่งต่างๆสับสนและสับสนให้มากที่สุด

ผมล้อเล่น :-)

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

เป็นกลไกค่าเรียงซ้อนแบบคลาสสิก การเลือกแต่ละรายการจะ จำกัด ตัวเลือกที่มีอยู่สำหรับการเลือกถัดไป ฯลฯ ดังนั้นฉันจึงปิดการใช้งานการควบคุมทั้งสองในแต่ละแถวถัดไปจนกว่า EditText ในแถวปัจจุบันจะมีค่า

ฉันต้องทำอย่างใดอย่างหนึ่งในสองสิ่งตามลำดับความต้องการนี้:

  1. เมื่อคลิกปุ่มให้ลบโฟกัสทันทีโดยไม่ต้องตั้งโฟกัสไปที่ปุ่มอื่น
  2. ตั้งโฟกัสไปที่ปุ่มแรกเมื่อเริ่มกิจกรรม

ปัญหาปรากฏขึ้นหลังจากส่งคืนกิจกรรมย่อย ปุ่มที่คลิกจะยังคงโฟกัส

Re: # 1 ด้านบน - ดูเหมือนจะไม่มีremoveFocus()วิธีการหรือสิ่งที่คล้ายกัน

Re: # 2 ข้างต้น - ฉันสามารถใช้requestFocus()เพื่อมุ่งเน้นชุดปุ่มบนแถวถัดไปและผลงานว่าหลังจากที่ผลตอบแทนย่อยกิจกรรม onCreate()แต่ด้วยเหตุผลบางอย่างมันไม่ได้ทำงานในกิจกรรมของผู้ปกครอง

ฉันต้องการความสอดคล้องของ UI ในทิศทางใดทิศทางหนึ่ง - ไม่มีปุ่มใดที่มีโฟกัสหลังจากกิจกรรมย่อยเสร็จสิ้นหรือแต่ละปุ่มได้รับโฟกัสขึ้นอยู่กับตำแหน่งของมันในโฟลว์ลอจิกรวมถึงปุ่มที่ใช้งานแรก (และเท่านั้น) ก่อนการเลือก

คำตอบ:


179

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

<LinearLayout 
    android:id="@+id/my_layout" 
    android:focusable="true" 
    android:focusableInTouchMode="true" ...>

ไปยังมุมมองเค้าโครงระดับบนสุดของฉัน (เลย์เอาต์เชิงเส้น) หากต้องการลบโฟกัสออกจากปุ่ม / EditTexts ฯลฯ ทั้งหมดคุณสามารถทำได้

LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();

การขอโฟกัสไม่ได้ทำอะไรเลยเว้นแต่ฉันจะตั้งค่ามุมมองให้โฟกัสได้


ชั่วขณะหนึ่งฉันคิดว่ามันอาจได้ผล “ ความเรียบง่ายสวยงามแค่ไหน!” ฉันพูดกับตัวเอง อนิจจาไม่เป็นเช่นนั้น แม้ว่าฉันจะลบ requestFocus () ปุ่มของแถวถัดไปจะได้รับโฟกัส นี่จะเป็นค่ากำหนดที่สอง แต่ถ้าฉันสามารถตั้งค่าโฟกัสที่ปุ่มแรกใน onCreate () ได้
InteXX

@InteXX: วันนี้ฉันเจอปัญหานี้อีกครั้งและพบวิธีแก้ปัญหา ฉันรู้ว่าตอนนี้คุณไปคนละเส้นทางแล้ว แต่ลองดูสิถ้าคุณเคยพบว่าตัวเองอยู่บนเรือลำเดียวกันอีก!
actionshrimp

"ฉันรู้ว่าคุณไปคนละเส้นทางแล้ว" ตอนนี้ยังไม่สายเกินไปที่จะสำรองข้อมูล :-) ฉันจะลองและแจ้งให้คุณทราบขอบคุณ
InteXX

ยี้. เกือบ แต่ไม่มาก ใช่มันจะโฟกัสออกจากปุ่มที่คลิกสุดท้าย แต่จะป้องกันไม่ให้ปุ่มใด ๆ ได้รับโฟกัส ซึ่งหมายความว่าผู้ใช้ที่ไม่ได้สัมผัสของฉันจะไม่สามารถคลิกปุ่มได้ มันทำเช่นเดียวกันกับคุณหรือไม่?
InteXX

6
สิ่งนี้จะไม่ทำงานบน ScrollView หากมุมมองด้านบนของคุณคือ ScrollView ให้ใช้สิ่งนี้กับลูกของมัน
UrošPodkrižnik

82

คำถามเก่า แต่ฉันเจอมันเมื่อฉันมีปัญหาที่คล้ายกันและคิดว่าฉันจะแบ่งปันสิ่งที่ฉันทำ

มุมมองที่ได้รับโฟกัสแตกต่างกันในแต่ละครั้งดังนั้นฉันจึงใช้คำทั่วไป:

View current = getCurrentFocus();
if (current != null) current.clearFocus();

12
วิธีที่หรูหราในการทำเช่นนี้ อาจต้องการตรวจสอบค่า null จาก getCurrentFocus () ก่อนที่จะเรียก clearFocus ()
Vering

5
+1 ใช่คุณจะต้องทำเครื่องหมายว่างนั้น! - เหตุใด Java จึงไม่สามารถใช้ safe-traversal-operator ไม่ได้? มันจะเรียบง่ายเหมือนgetCurrentFocus()?.clearFocus();อย่างสวยงามและสง่างาม: - /
Levite

1
@willlma: ใช่นี่คือสิ่งที่ฉันหมายถึง
ตรงกับ

5
@ เลวิตไวมากเหมือน :-) แต่แม้ว่า Java จะใช้ตัวดำเนินการดังกล่าว แต่ก็ยังต้องใช้เวลา 4 ปีในการติดตาม Android
Greg Brown

2
@ เลวิตตอนนี้คุณคงเจอแล้ว แต่ก็อตลิน
prodaea

9
  1. คุณสามารถใช้View.clearFocus().

  2. ใช้View.requestFocus()เรียกจากonResume().


@siliconeagle: ตอนนี้มันแปลกมาก (ขอบคุณสำหรับตัวชี้เพื่อ clearFocus () btw - ฉันไม่ทราบเรื่องนี้) เมื่อฉันคลิกปุ่มแรกทำการเลือกแล้วย้อนกลับปุ่มแรกจะไม่สามารถรับโฟกัสได้อีกต่อไป ฉันไม่ได้ตั้งค่าที่สามารถโฟกัสได้สำหรับปุ่มนี้ในโค้ดของฉัน ฉันต้องการแสดงรหัสของฉัน แต่มีตัวอักษรไม่เพียงพอในหน้าต่างแก้ไขนี้และยังใหม่สำหรับฉันจึงไม่แน่ใจว่าควรป้อนคำตอบอื่นเพื่อให้สามารถโพสต์รหัสได้หรือไม่
InteXX

@siliconeagle: น่าเสียดายที่ clearFocus () ไม่ทำงานปุ่มจะคงโฟกัสไว้เมื่อกิจกรรมย่อยกลับมา
InteXX

@siliconeagle: โอเคไม่เป็นไรเกี่ยวกับความคิดเห็นแรกนั้น ฉันใช้ setFocusable (false) ในเหตุการณ์การคลิกของปุ่มและลืมที่จะเปิดอีกครั้งหลังจากที่กิจกรรมย่อยกลับมา clearFocus () ยังไม่มีผล - ฉันสงสัยว่าทำไม? ฉันเรียกมันบนทุกปุ่มจาก onActivityResult () แต่ปุ่มที่คลิกยังคงโฟกัสอยู่ แปลก
InteXX

@siliconeagle: @actionshrimp: ฉันตัดสินใจเลิกใช้คำสั่งและการตั้งค่าที่เกี่ยวข้องกับโฟกัสทั้งหมด ความตั้งใจเดิมของฉันคือการป้องกันไม่ให้โฟกัสไปที่ปุ่มที่ปิดใช้งาน แต่ถ้าเป็นค่าใช้จ่ายมันก็ไม่คุ้ม หากไม่มี requestFocus (), clearFocus () หรือ setFocusable () จะไม่มีปุ่มใดที่จะคงโฟกัสไว้หลังจากที่กิจกรรมย่อยกลับมา มันเป็นด้วงน้อยกว่าสองตัว - ฉันเดาว่าฉันจะต้องอยู่กับผู้ใช้ที่สามารถตั้งโฟกัสเป็นปุ่มปิดใช้งานได้
InteXX

องค์ประกอบที่ปิดใช้งานไม่ได้ AFAIK ที่โฟกัสได้ ... ใช้ setEnabled (false)
siliconeagle

9

android:descendantFocusability="beforeDescendants"

การใช้สิ่งต่อไปนี้ในกิจกรรมกับตัวเลือกเค้าโครงบางอย่างด้านล่างดูเหมือนจะได้ผลตามที่ต้องการ

 getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();

โดยเชื่อมต่อกับพารามิเตอร์ต่อไปนี้ในมุมมองรูท

<?xml
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:descendantFocusability="beforeDescendants" />

https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability

ตอบขอบคุณ: https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text

เกี่ยวกับ windowSoftInputMode

ยังมีประเด็นสำคัญอีกประการที่ต้องระวัง ตามค่าเริ่มต้น Android จะกำหนดโฟกัสเริ่มต้นให้กับ EditText แรกโดยอัตโนมัติหรือการควบคุมที่โฟกัสได้ในกิจกรรมของคุณ ตามธรรมชาติแล้ววิธีการป้อนข้อมูล (โดยทั่วไปคือแป้นพิมพ์อ่อน) จะตอบสนองต่อเหตุการณ์โฟกัสโดยแสดงตัวเอง แอตทริบิวต์ windowSoftInputMode ใน AndroidManifest.xml เมื่อตั้งค่าเป็น stateAlwaysHidden จะสั่งให้แป้นพิมพ์ละเว้นโฟกัสเริ่มต้นที่กำหนดโดยอัตโนมัตินี้

<activity
    android:name=".MyActivity"
    android:windowSoftInputMode="stateAlwaysHidden"/>

ข้อมูลอ้างอิงที่ดี


6
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:id="@+id/ll_root_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">

LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();

ฉันใช้สิ่งนี้เมื่ออัปเดตข้อมูลโปรไฟล์เสร็จแล้วและลบโฟกัสทั้งหมดออกจาก EditText ในเค้าโครงของฉัน

====> อัปเดต: ในเนื้อหาโครงร่างหลักของฉันเพิ่มบรรทัด EditText:

android:focusableInTouchMode="true"

3

สิ่งที่เกี่ยวกับการเพิ่มandroid:windowSoftInputMode="stateHidden"กิจกรรมของคุณในรายการ

นำมาจากคนเก่งที่แสดงความคิดเห็นเกี่ยวกับสิ่งนี้: https://stackoverflow.com/a/2059394/956975


3

ฉันพยายามปิดใช้งานและเปิดใช้งานความสามารถในการโฟกัสสำหรับมุมมองและมันใช้ได้สำหรับฉัน (โฟกัสถูกรีเซ็ต):

focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);

2

ก่อนอื่นจะได้ผล 100% ........

  1. สร้างonResume()วิธีการ
  2. ภายในนี้หามุมมองที่จะเน้นอีกครั้งและอีกครั้งโดยonResume()findViewById()
  3. ภายในonResume()ชุดrequestFocus()นี้เป็นมุมมองนี้
  4. ภายในonResume()ชุดclearFocusนี้เป็นมุมมองนี้
  5. ไปที่ xml ของเลย์เอาต์เดียวกันและค้นหามุมมองด้านบนที่คุณต้องการโฟกัสและตั้งค่าfocusabletrue และfocusableInTuchtrue
  6. ภายในนี้onResume()ค้นหามุมมองด้านบนด้านบนโดยfindViewById
  7. ภายในonResume()ชุดrequestFocus()นี้เป็นมุมมองนี้ที่สุดท้าย
  8. และตอนนี้สนุก ......

2
android:focusableInTouchMode="true"
android:focusable="true"
android:clickable="true"

เพิ่มลงใน ViewGroup ของคุณที่มี EditTextView ของคุณ มันทำงานได้อย่างถูกต้องกับเค้าโครงข้อ จำกัด ของฉัน หวังว่าจะช่วยได้


1

คุณสามารถลองปิดความสามารถของกิจกรรมหลักในการบันทึกสถานะได้ (ทำให้ลืมว่าตัวควบคุมใดมีข้อความและสิ่งที่โฟกัสอยู่) คุณจะต้องมีวิธีอื่นในการจดจำว่า EditText ของคุณมีอะไรบ้างและนำข้อมูลเหล่านี้มาเติมบนResume () เปิดตัวกิจกรรมย่อยของคุณด้วย startActivityForResult () และสร้างตัวจัดการ onActivityResult () ในกิจกรรมหลักของคุณซึ่งจะอัปเดต EditText ให้ถูกต้อง ด้วยวิธีนี้คุณสามารถตั้งค่าปุ่มที่เหมาะสมที่คุณต้องการเน้นที่Resume () ในเวลาเดียวกันกับที่คุณเติม EditText ใหม่โดยใช้ myButton.post (Runnable ใหม่ () {run () {myButton.requestFocus ();}});

เมธอด View.post () มีประโยชน์สำหรับการตั้งค่าโฟกัสในตอนแรกเนื่องจาก runnable นั้นจะถูกเรียกใช้งานหลังจากสร้างหน้าต่างแล้วและสิ่งต่างๆจะสงบลงทำให้กลไกการโฟกัสทำงานได้อย่างถูกต้องตามเวลานั้น ฉันพบการพยายามตั้งค่าโฟกัสระหว่าง onCreate / Start / Resume ()

โปรดทราบว่านี่เป็นรหัสหลอกและไม่ผ่านการทดสอบ แต่เป็นแนวทางที่เป็นไปได้ที่คุณสามารถลองได้


ในความคิดที่สอง ... ลองใช้สิ่ง View.post () ก่อนแทนที่จะปิดสถานะที่บันทึกไว้ของกิจกรรม นั่นอาจเป็นเคล็ดลับสำหรับคุณด้วยตัวเอง
Uncle Code Monkey

อนิจจาฉันไม่มีวิธีง่ายๆในการทดสอบสิ่งนี้สำหรับเราอีกต่อไป ตั้งแต่นั้นมาฉันได้ตัดสินใจที่จะละทิ้งรูปแบบโค้ดเนทีฟและไปใช้ HTML5 / CSS3 สำหรับแอพมือถือดังนั้นจึงได้เปิดระบบของฉันใหม่ตามนั้น แต่ขอขอบคุณสำหรับสิ่งที่ดูเหมือนจะเป็นคำตอบในเชิงลึก ฉันโหวตมันขึ้น
InteXX

0

ลองทำดังต่อไปนี้ (โทรclearAllEditTextFocuses();)

private final boolean clearAllEditTextFocuses() {
    View v = getCurrentFocus();
    if(v instanceof EditText) {
        final FocusedEditTextItems list = new FocusedEditTextItems();
        list.addAndClearFocus((EditText) v);

        //Focus von allen EditTexten entfernen
        boolean repeat = true;
        do {
            v = getCurrentFocus();
            if(v instanceof EditText) {
                if(list.containsView(v))
                    repeat = false;
                else list.addAndClearFocus((EditText) v);
            } else repeat = false;
        } while(repeat);

        final boolean result = !(v instanceof EditText);
        //Focus wieder setzen
        list.reset();
        return result;
    } else return false;
}

private final static class FocusedEditTextItem {

    private final boolean focusable;

    private final boolean focusableInTouchMode;

    @NonNull
    private final EditText editText;

    private FocusedEditTextItem(final @NonNull EditText v) {
        editText = v;
        focusable = v.isFocusable();
        focusableInTouchMode = v.isFocusableInTouchMode();
    }

    private final void clearFocus() {
        if(focusable)
            editText.setFocusable(false);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(false);
        editText.clearFocus();
    }

    private final void reset() {
        if(focusable)
            editText.setFocusable(true);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(true);
    }

}

private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {

    private final void addAndClearFocus(final @NonNull EditText v) {
        final FocusedEditTextItem item = new FocusedEditTextItem(v);
        add(item);
        item.clearFocus();
    }

    private final boolean containsView(final @NonNull View v) {
        boolean result = false;
        for(FocusedEditTextItem item: this) {
            if(item.editText == v) {
                result = true;
                break;
            }
        }
        return result;
    }

    private final void reset() {
        for(FocusedEditTextItem item: this)
            item.reset();
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.