ทำไม LayoutInflater จึงเพิกเฉยต่อพารามิเตอร์เลย์เอาต์ layout_width และ layout_height ที่ฉันระบุ?


168

ฉันมีปัญหาร้ายแรงในการทำให้ LayoutInflater ทำงานตามที่คาดไว้และคนอื่น ๆ ก็ทำเช่นนั้น: จะใช้ layoutinflator เพื่อเพิ่มมุมมองได้อย่างไร .

เหตุใด LayoutInflater จึงไม่สนใจพารามิเตอร์โครงร่างที่ฉันได้ระบุไว้ ยกตัวอย่างเช่นทำไมXML layout_widthและlayout_heightค่าจากทรัพยากรของฉันจึงไม่ได้รับเกียรติ


จริง - เจ็บหรือว่าฉันเลือกผิดหรือเปล่า? แค่คิดว่าฉันจะแบ่งปันผลลัพธ์ของฉัน ต้องยอมรับบทเรียนที่ไม่ได้กล่าวถึงเฉพาะในหน้าคำถามที่พบบ่อย ..
andig

3
ดูย่อหน้าสุดท้ายของคำถามแรกในคำถามที่พบบ่อย โพสต์เป็นคำถามแล้วโพสต์บทช่วยสอนเป็นคำตอบ แน่นอนคุณสามารถแก้ไขการตัดคำถามและวางบทช่วยสอนในคำตอบ ฉันจะลงคะแนนหากคุณแก้ไขปัญหานี้
bigstones

คำตอบ:


381

ฉันตรวจสอบปัญหานี้แล้วโดยอ้างถึงLayoutInflater docsและตั้งค่าโครงการสาธิตตัวอย่างขนาดเล็ก LayoutInflaterแสดงให้เห็นว่าบทเรียนต่อไปนี้วิธีการแบบไดนามิกเติมรูปแบบโดยใช้

ก่อนที่เราจะเริ่มต้นดูว่าLayoutInflater.inflate()พารามิเตอร์มีลักษณะอย่างไร:

  • ทรัพยากร : รหัสสำหรับทรัพยากรรูปแบบ XML ที่จะโหลด (เช่นR.layout.main_page)
  • root : มุมมองทางเลือกที่จะเป็นพาเรนต์ของลำดับชั้นที่สร้างขึ้น (ถ้าattachToRootมีtrue) หรืออื่น ๆ เป็นเพียงวัตถุที่มีชุดของLayoutParamsค่าสำหรับรูทของลำดับชั้นที่ส่งคืน (ถ้าattachToRootมีfalse)
  • attachToRoot : ว่าควรแนบลำดับชั้นที่สูงเกินจริงกับพารามิเตอร์รูทหรือไม่? หากเป็นเท็จรูตจะใช้เพื่อสร้างคลาสย่อยที่ถูกต้องของLayoutParamsสำหรับมุมมองรูทใน XML

  • คืนค่า : มุมมองรูทของลำดับชั้นที่สูงเกินจริง หากรูทถูกระบุและattachToRootเป็นtrueนี่คือรูท มิฉะนั้นจะเป็นรูทของไฟล์ XML ที่เกินจริง

ตอนนี้สำหรับตัวอย่างเค้าโครงและรหัส

เค้าโครงหลัก ( main.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</LinearLayout>

เพิ่มลงในคอนเทนเนอร์นี้เป็น TextView แยกต่างหากซึ่งสามารถมองเห็นเป็นสี่เหลี่ยมจัตุรัสสีแดงเล็ก ๆ ได้หากใช้พารามิเตอร์โครงร่างจาก XML ( red.xml) ได้สำเร็จ:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="25dp"
    android:layout_height="25dp"
    android:background="#ff0000"
    android:text="red" />

ตอนนี้LayoutInflaterใช้กับพารามิเตอร์การโทรหลายรูปแบบ

public class InflaterTest extends Activity {

    private View view;

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

      setContentView(R.layout.main);
      ViewGroup parent = (ViewGroup) findViewById(R.id.container);

      // result: layout_height=wrap_content layout_width=match_parent
      view = LayoutInflater.from(this).inflate(R.layout.red, null);
      parent.addView(view);

      // result: layout_height=100 layout_width=100
      view = LayoutInflater.from(this).inflate(R.layout.red, null);
      parent.addView(view, 100, 100);

      // result: layout_height=25dp layout_width=25dp
      // view=textView due to attachRoot=false
      view = LayoutInflater.from(this).inflate(R.layout.red, parent, false);
      parent.addView(view);

      // result: layout_height=25dp layout_width=25dp 
      // parent.addView not necessary as this is already done by attachRoot=true
      // view=root due to parent supplied as hierarchy root and attachRoot=true
      view = LayoutInflater.from(this).inflate(R.layout.red, parent, true);
    }
}

ผลลัพธ์ที่แท้จริงของการเปลี่ยนแปลงของพารามิเตอร์มีการบันทึกไว้ในรหัส

สรุป: การโทรLayoutInflaterโดยไม่ระบุรูตจะทำให้เกิดการโทรเกินจริงโดยไม่สนใจพารามิเตอร์โครงร่างจาก XML การเรียกใช้ inflate ด้วยรูทไม่เท่ากันnullและattachRoot=trueโหลดพารามิเตอร์โครงร่าง แต่ส่งคืนออบเจ็กต์รูทอีกครั้งซึ่งป้องกันการเปลี่ยนโครงร่างเพิ่มเติมไปยังออบเจกต์ที่โหลด (เว้นแต่คุณจะพบว่าใช้งานfindViewById()) ระเบียบการโทรที่คุณมักจะต้องการใช้จึงเป็นแบบนี้:

loadedView = LayoutInflater.from(context)
                .inflate(R.layout.layout_to_load, parent, false);

เพื่อช่วยในเรื่องการจัดวางขอแนะนำให้ผู้ตรวจสอบเค้าโครง


20
คำตอบที่ยอดเยี่ยม เคยทำงานกับ Android เป็นเวลา 3 ปีแล้วนับและยังมีบางสิ่งที่ต้องเรียนรู้จากสิ่งนี้
รูเบน Scratton

สวัสดีมีวิธีใดที่จะทำสิ่งเดียวกันกับรหัส java แทนที่จะพองตัวด้วย R.layout เพราะฉันไม่มีเลย์เอาต์ ฉันได้สร้างมุมมองด้วยรหัส java และฉันต้องการเพิ่มมุมมองเดียวกันเป็นเวลา 300 ครั้ง
Raj

1
@ andig: Activityเป็นคลาสย่อยContextและตัวอย่างของคำตอบอยู่ในขอบเขตActivityดังนั้นเพื่อให้ง่ายขึ้นฉันแทนที่getBaseContext()ด้วยthisเป็นเพื่อประโยชน์ของคำตอบนี้มันเทียบเท่า
Marcin Orlowski

5
คอนเทนเนอร์มี layout_width และ layout_height ตั้งค่าเป็น "match_parent"; ความคิดเห็นพูดว่า "// result: layout_height = wrap_content layout_width = match_parent" ฉันหายไปหรือไม่
Marian Paździoch

1
ขอบคุณสำหรับการทำวิจัยและแบ่งปันผลลัพธ์! นอกจากนี้ฉันคำถามที่สองของแมเรียน
LarsH

5

andig ถูกต้องที่เหตุผลทั่วไปสำหรับ LayoutInflater ละเว้น layout_params ของคุณอาจเป็นเพราะไม่ได้ระบุรูท หลายคนคิดว่าคุณสามารถผ่านเป็นศูนย์ได้ สิ่งนี้ยอมรับได้สำหรับบางสถานการณ์เช่นกล่องโต้ตอบซึ่งคุณไม่สามารถเข้าถึงรูทได้ในขณะที่สร้าง อย่างไรก็ตามกฎที่ดีในการทำตามคือถ้าคุณมีรูทให้ไปที่ LayoutInflater

ฉันเขียนโพสต์บล็อกเชิงลึกเกี่ยวกับเรื่องนี้ที่คุณสามารถตรวจสอบได้ที่นี่:

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/


1
This is acceptable for a few scenarios such as a dialogนั่นคือทั้งหมดที่ฉันต้องการ
rookieDeveloper

0

ต้องการเพิ่มคำตอบหลักข้างต้น
ฉันพยายามติดตาม แต่ recyclerView ของฉันเริ่มยืดทุกรายการไปยังหน้าจอ
ฉันต้องเพิ่มบรรทัดถัดไปหลังจากพองตัวเพื่อเข้าถึงเป้าหมาย

itemLayoutView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));

ฉันเพิ่มพารามิเตอร์เหล่านี้ด้วย xml แล้ว แต่การทำงานไม่ถูกต้อง
และด้วยบรรทัดนี้ก็ถือว่าใช้ได้

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