2013-03-27 17 views
8

Tworzę menu, które zawiera niestandardowy adapter ExpandableListView. Pomimo tego, że próbowałem dopasować mój kod do przykładów API i innych przykładów, które widziałem w Internecie (w tym wiele blisko powiązanych pytań SO), nadal nie mogę tego zrobić.getChildView nie jest wywoływany

Wiem, że adapter jest używany, ponieważ wyświetlany jest widok grupy (utworzony z xml). Kliknięcie elementu grupy powoduje również wywołanie "getGroupView", ale kod nigdy nie uruchamia "getChildView", "getChild" ani "getChildId".

Przeszedłem nawet przez kod źródłowy Android-15, aby dowiedzieć się, co mogłem zrobić źle, ale nic dziwnego nie wyszło.

= Activity Base

public class SettingsM extends FragmentActivity 
{ 
    static Context context; 
    ViewPager mViewPager; 
    CollectionPagerAdapter mDemoCollectionPagerAdapter; 
    //ColorPicker picker; 

    SharedPreferences preferences; 

    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
      WindowManager.LayoutParams.FLAG_FULLSCREEN); 

     context = this; 
     // Set up action bar. 
     final ActionBar actionBar = getActionBar(); 

     actionBar.setDisplayHomeAsUpEnabled(false); 
     actionBar.setDisplayUseLogoEnabled(false); 
     actionBar.setTitle("Particle emitter settings"); 

     // get the preferences for this screen 
     preferences = this.getSharedPreferences("base_world", 0); 

     // tab holder 
     setContentView(R.layout.tabmenu_holder); 
     mViewPager = (ViewPager) findViewById(R.id.pager); 

     mDemoCollectionPagerAdapter = new CollectionPagerAdapter(getSupportFragmentManager()); 
     mViewPager.setAdapter(mDemoCollectionPagerAdapter); 
    } 

    public class CollectionPagerAdapter extends FragmentStatePagerAdapter 
    { 
     public CollectionPagerAdapter(FragmentManager fm) 
     { 
      super(fm); 
     } 

     @Override 
     public Fragment getItem(int i) 
     { 
      Fragment fragment = new TabFragment(); 
      Bundle args = new Bundle(); 
      if(preferences != null) 
       { 
       if(i == 0) 
       {// world 
        args.putBoolean("world", true); 
       } else 
       {// emitter 
        args.putBoolean("world", false); 
        args.putInt("emitter", i-1); 
       } 
      } 
      fragment.setArguments(args); 
      return fragment; 
     } 

     @Override 
     public int getCount() 
     { 
      return 100; 
     } 

     @Override 
     public CharSequence getPageTitle(int position) 
     { 
      if(position == 0) 
      { 
       return "World"; 
      } else 
      { 
       return "Emitter #" + position; 
      } 
     } 
    } 

    public static class TabFragment extends Fragment 
    {   
     @Override 
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
     { 
      Bundle args = getArguments(); 
      View rootView = new TextView(context);// use a textview as the default 

      if(args.getBoolean("world")) 
      { 
       rootView = inflater.inflate(R.layout.m_backdrop, container, false); 

       // add face drop-down 
       ExpandableListView face_list = (ExpandableListView) rootView.findViewById(R.id.FaceList); 
       if(face_list != null) 
       { 
        face_list.setAdapter(new ExpandableFaceList(context)); 
        face_list.setOnGroupClickListener(new OnGroupClickListener() 
        { 

         @Override 
         public boolean onGroupClick(ExpandableListView parent, 
          View v, int groupPosition, long id) 
         { 
          Log.i("FaceList", "Clicked:" + groupPosition); 
          return false; 
         } 
        }); 
       } 

      } else 
      { 
       return rootView; 
      } 
      return rootView; 
     } 
    } 
} 

= Lista rozbudowy widok Adapter

public class ExpandableFaceList extends BaseExpandableListAdapter implements ExpandableListAdapter 
{ 
    public Context context; 
    private LayoutInflater inflator; 
    private float mDensity = 1f; 

    private boolean bShowSw = true; 
    private ColorPickerMenuView cp; 
    private Switch sw; 


    public ExpandableFaceList(Context context) 
    { 
     this.context = context; 
     this.inflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

     mDensity = context.getResources().getDisplayMetrics().density; 
    } 

    @Override 
    public int getGroupCount() 
    { 
     return 1; 
    } 

    @Override 
    public int getChildrenCount(int groupPosition) 
    { 
     return 6;// because there are 6 faces to a cube 
    } 

    // list views 
    @Override 
    public View getGroupView(int groupPosition, boolean isExpanded, 
      View convertView, ViewGroup parent) 
    { 
     View v = convertView; 
     TextView tv; 

     if(v == null) 
     { 
      v = inflator.inflate(R.layout.exlist_head, parent, false); 

      tv = (TextView) v.findViewById(R.id.exList_Title); 
      if(tv != null) 
       tv.setText("Face Colors"); 

      tv = (TextView) v.findViewById(R.id.exList_Summary); 
      String s_text = "The color for each face, click to "; 
      s_text += ((isExpanded)? "collaspe": "expand"); 
      if(tv != null) 
       tv.setText(s_text); 
     } else 
     { 
      tv = (TextView) v.findViewById(R.id.exList_Summary); 
      String s_text = "The color for each face, click to "; 
      s_text += ((isExpanded)? "collaspe": "expand"); 
      if(tv != null) 
       tv.setText(s_text); 
      parent.invalidate(); 
     }   

     return v; 
    } 

    @Override 
    public View getChildView(int groupPosition, int childPosition, 
      boolean isLastChild, View convertView, ViewGroup parent) 
    { 
     View v = convertView; 

     if(v == null) 
     { 
      if(bShowSw) 
      { 
       v = (LinearLayout) inflator.inflate(R.layout.face_info_sw, parent, false); 
      } else 
      { 
       v = (LinearLayout) inflator.inflate(R.layout.face_info, parent, false); 
      } 
     } 

     switch(childPosition) 
     { 
     case 0: 
      v.setTag("Front"); 
      break; 
     case 1: 
      v.setTag("Back"); 
      break; 
     case 2: 
      v.setTag("Left"); 
      break; 
     case 3: 
      v.setTag("Right"); 
      break; 
     case 4: 
      v.setTag("Top"); 
      break; 
     case 5: 
      v.setTag("Bottom"); 
      break; 
      } 

     Log.i("ELV", "Pos:" + childPosition); 

     cp = (ColorPickerMenuView) v.findViewById(R.id.face_color); 
     cp.setTitle((String) v.getTag()); 

     cp.setOnClickListener(new OnClickListener() { 
      public void onClick(View v) { UpdateData(v); } 
     }); 

     if(bShowSw) 
     { 
      sw = (Switch) v.findViewById(R.id.face_sw); 
      sw.setOnClickListener(new OnClickListener() { 
       public void onClick(View v) { UpdateData(v); } 
      }); 
     } 

     return v; 
    } 

    private void UpdateData(View v) { } 

    @Override 
    public Object getGroup(int groupPosition) 
    { 
     return groupPosition; 
    } 

    @Override 
    public Object getChild(int groupPosition, int childPosition) 
    { 
     return "Child:" + groupPosition + "." + childPosition; 
    } 

    @Override 
    public long getGroupId(int groupPosition) 
    { 
     return groupPosition; 
    } 

    @Override 
    public long getChildId(int groupPosition, int childPosition) 
    { 
     return childPosition; 
    } 

    @Override 
    public boolean hasStableIds() 
    { 
     return true; 
    } 

    @Override 
    public boolean isChildSelectable(int groupPosition, int childPosition) 
    { 
     return true; 
    } 
} 

= działanie w xml widok

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/pager" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <android.support.v4.view.PagerTitleStrip android:id="@+id/pager_title_strip" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:layout_gravity="top" 
     android:background="#33b5e5" 
     android:textColor="#fff" 
     android:paddingTop="4dp" 
     android:paddingBottom="4dp" /> 

</android.support.v4.view.ViewPager> 

= m_backdrop.xml (Menu dla ustawienia t)

<?xml version="1.0" encoding="utf-8"?> 
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    xmlns:app="http://schemas.android.com/apk/res/com.zyphronics.aquafinger"> 

     <RelativeLayout 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:orientation="vertical" > 

      <TextView 
      android:id="@+id/Title_GeneralSettings_text" 
      style="?android:attr/listSeparatorTextViewStyle" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_marginTop="5dip" 
      android:text="General Settings" /> 

     <ExpandableListView 
      android:id="@+id/FaceList" 
      android:layout_width="match_parent" 
      android:layout_height="fill_parent" 
      android:layout_below="@id/Title_GeneralSettings_text" > 
     </ExpandableListView> 

     </RelativeLayout> 

</ScrollView> 

= face_info.sw.xml (The face_info.xml jest taka sama, ale bez przełącznika)

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:orientation="vertical" > 

    <Switch 
     android:id="@+id/face_sw" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:layout_marginLeft="10dp" 
     android:tag="face_sw" 
     android:text="Enabled?" 
     android:textOff="No" 
     android:textOn="Yes" /> 

    <com.zyphronics.AF.Controls.colorpicker.ColorPickerMenuView 
     android:id="@+id/face_color" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:tag="face_color" /> 
</LinearLayout> 

Nawet bez "ColorPickerMenuView" części w face_info_sw.xml, widok dziecko nadal nie jest być nazwanym. Próbowałem też samemu mieć układ względny, zamiast w widoku przewijania, ale uniemożliwiło to przewijanie kolejnych dodawanych elementów (a także niestandardowe formanty mają dziwne rozmiary).

Każda pomoc w rozwiązaniu tego problemu byłaby świetna. Jeśli ktokolwiek potrzebuje spakowanego źródła tego (kompatybilnego z Eclipse), tak jak w komentarzach, a ja odpowiem linkiem.

Odpowiedz

6

Bummer, że nikt nie był w stanie odpowiedzieć na to pytanie, ale na szczęście udało mi się znaleźć rozwiązanie tego problemu, aby inni mogli dodawać rozszerzalne widoki list również do ich fragmentów.

Znalazłem rozwiązanie podczas sprawdzania kodu źródłowego dla przędzarki (ponieważ chciałem zrobić spinner z własnymi wpisami). Kluczowy punkt w kodzie był w "onMeasure", kiedy mierzy on wszystkie jego dzieci ... no, aż do predefiniowanego limitu 15, ale pomiar dzieci jest najważniejszy.

= Rozwiązanie = Podczas tworzenia adaptera należy przekazać widok macierzysty. Jest to klucz, ponieważ jest to jedyny link do widoku z adaptera (tylko łatwy sposób).

public Context context; 
public ExpandableListView parent; 
private LayoutInflater inflator; 
// Only measure this many items to get a decent max height. 
private static final int MAX_ITEMS_MEASURED = 15; 

public ExpandableFaceList(Context context, final ExpandableListView parent) 
{ 
    this.context = context; 
    this.parent = parent; 
    this.inflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

    mDensity = context.getResources().getDisplayMetrics().density; 
} 

Następnie w „getGroupView”, ponieważ wydaje się być jedyną rzeczą, która jest powszechnie nazywany, będziesz musiał dodać następujący kod:

public View getGroupView(int groupPosition, boolean isExpanded, 
View convertView, ViewGroup parent) 
{ 
    if(convertView != null) 
    { 
     int nH = 0; 
     if(isExpanded) 
     { 
      nH = measureChildrenHeight(groupPosition); 
     } else 
     { 
      nH = convertView.getMeasuredHeight(); 
     } 
     parent.getLayoutParams().height = (int) (nH * mDensity); 

     parent.invalidate(); 
    } 

    return convertView; 
} 

Kluczowym elementem w powyższy kod to "measureChildrenHeight", "convertView.getMeasuredHeight()" służy tylko do przywrócenia ELV po zwinięciu.Kod Środek dzieci jest następujący:

int measureChildrenHeight(int groupPosition) 
{ 
    int height = 0; 
    View itemView = null; 
    LinearLayout viewGroup = new LinearLayout(context); 
    viewGroup.setOrientation(LinearLayout.VERTICAL); 

    final int widthMeasureSpec = 
    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 
    final int heightMeasureSpec = 
    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 

    // Make sure the number of items we'll measure is capped. If it's a huge data set 
    // with wildly varying sizes, oh well. 
    int start = 0;//Math.max(0, getSelectedItemPosition()); 

    final int end = Math.min(getChildrenCount(groupPosition), start + MAX_ITEMS_MEASURED); 
    final int count = end - start; 
    start = Math.max(0, start - (MAX_ITEMS_MEASURED - count)); 
    for (int i = start; i < end; i++) 
    { 
     itemView = getChildView(groupPosition, i, (i+1 == end), itemView, viewGroup); 
     if (itemView.getLayoutParams() == null) 
     { 
      itemView.setLayoutParams(new ViewGroup.LayoutParams(
      ViewGroup.LayoutParams.WRAP_CONTENT, 
      ViewGroup.LayoutParams.WRAP_CONTENT)); 
     } 
     itemView.measure(widthMeasureSpec, heightMeasureSpec); 

     if(i+1 != end) 
     height += itemView.getMeasuredHeight();//Math.max(height, itemView.getMeasuredHeight()); 
    } 

    return height; 
} 

Uwaga: musiałem dostosować kod do wyłączenia wysokość ostatniej pozycji, ponieważ to było dodanie dodatkowej przestrzeni poniżej ostatniej pozycji. Jeśli masz dzieci różnej wysokości, mogą pojawić się problemy. Nadal mam trochę pustego miejsca pod nagłówkiem dzieci/grupy, ale jest to wybaczalne (w przeciwieństwie do poprzedniej kwoty, która miała 3-4 dzieci wzrostu).

+0

http://stackoverflow.com/a/14159666/390014 pomaga dla nieprawidłowo obliczonej wysokości – draw

+0

nie działa dla mnie, gdzie jest używany obiekt macierzysty ExpandableListView, który jest przekazywany z konstruktora? –

17

Problem polega na tym, że twój ExpandableListView jest w ScrollView. W większości przypadków jest to wynikiem złych układów i powinieneś tego unikać.

Jednak czasami jest to jedyne rozwiązanie (w rzeczywistości nie jest to prawda, ale inne rozwiązania mogą być poza zakresem). Twoje rozwiązanie działa również, ale jest nieco skomplikowane. Zamiast pisać niestandardowe ExpandableListView takiego:

public class ExpandExpandableListView extends ExpandableListView{ 
    boolean expanded = true; 

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

    public ExpandExpandableListView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

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

    public boolean isExpanded() 
    { 
     return expanded; 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     if (isExpanded()) 
     { 
      // Calculate entire height by providing a very large height hint. 
      // View.MEASURED_SIZE_MASK represents the largest height possible. 
      int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST); 
      super.onMeasure(widthMeasureSpec, expandSpec); 

      ViewGroup.LayoutParams params = getLayoutParams(); 
      params.height = getMeasuredHeight(); 
     }else{ 
      super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     } 
    } 

    public void setExpanded(boolean expanded) 
    { 
     this.expanded = expanded; 
    } 
} 

iw swoim m_backdrop.xml:

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

     <RelativeLayout 
     ... 

     <com.package.name.to.class.ExpandExpandableListView 
      android:id="@+id/FaceList" 
      android:layout_width="match_parent" 
      android:layout_height="fill_parent" 
      android:layout_below="@id/Title_GeneralSettings_text" > 
     </ExpandableListView> 

     </RelativeLayout> 

</ScrollView> 

to już to. Powinno działać zgodnie z oczekiwaniami.