web-dev-qa-db-fra.com

android listview afficher tous les éléments disponibles sans défilement avec en-tête statique

J'ai un peu de mal à faire en sorte qu'une certaine mise en page fonctionne: je veux une liste. La liste ne doit pas nécessairement être déroulante, mais doit être affichée complètement. Mais la page elle-même devrait pouvoir défiler (avec les listes qu’elle contient), si le contenu total est plus élevé que l’écran. 

<ScrollView
     xmlns:Android="http://schemas.Android.com/apk/res/Android"
     Android:layout_width="fill_parent"
     Android:layout_height="wrap_content"
     >

     <LinearLayout
         xmlns:Android="http://schemas.Android.com/apk/res/Android"
         Android:id="@+id/linear_layout"
         Android:orientation="vertical"
         Android:layout_width="fill_parent"
         Android:layout_height="wrap_content"
         Android:layout_weight="1"
         Android:background="#ff181818"
         >
           <Textview Android:id="@+id/my_text" text="header contents goes here" Android:layout_width="fill_parent" Android:layout_height="wrap_content"/>
           <Textview Android:id="@+id/headertext" text="header contents goes here" Android:layout_width="fill_parent" Android:layout_height="wrap_content"/>

          <ListView
               Android:id="@+id/my_list1"
               Android:layout_height="wrap_content"
               Android:layout_width="fill_parent"
          /> 
     </LinearLayout> 

</ScrollView>

il n'utilise qu'une petite partie de l'écran (environ 2 lignes par liste), au lieu de remplir la hauteur disponible, et les listes elles-mêmes peuvent être défilées. Comment puis-je changer la mise en page pour toujours afficher les listes entières mais que l'écran soit scrollalbe? 

32
d-man

La solution que j'ai utilisée consiste à remplacer ListView par LinearLayout. Vous pouvez créer tous vos éléments dans LinearLayout, ils seront tous affichés. Donc, il n'est vraiment pas nécessaire d'utiliser ListView.

LinearLayout list = (LinearLayout)findViewById(R.id.list_recycled_parts);
for (int i=0; i<products.size(); i++) {
  Product product = products.get(i);
  View vi = inflater.inflate(R.layout.product_item, null);
  list.addView(vi);
}
62
Fedor

Comme @Alex l'a noté dans la réponse acceptée, LinearLayout n'est guère un remplacement. J'ai eu un problème où LinearLayout n'était pas une option, c'est quand je suis tombé sur ce blog . Je vais mettre le code ici à des fins de référence. J'espère que ça aide quelqu'un là-bas!

public class UIUtils {

    /**
     * Sets ListView height dynamically based on the height of the items.
     *
     * @param listView to be resized
     * @return true if the listView is successfully resized, false otherwise
     */
    public static boolean setListViewHeightBasedOnItems(ListView listView) {

        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter != null) {

            int numberOfItems = listAdapter.getCount();

            // Get total height of all items.
            int totalItemsHeight = 0;
            for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
                View item = listAdapter.getView(itemPos, null, listView);
                item.measure(0, 0);
                totalItemsHeight += item.getMeasuredHeight();
            }

            // Get total height of all item dividers.
            int totalDividersHeight = listView.getDividerHeight() *
                    (numberOfItems - 1);

            // Set list height.
            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalItemsHeight + totalDividersHeight;
            listView.setLayoutParams(params);
            listView.requestLayout();

            return true;

        } else {
            return false;
        }

    }
}

Usage:

//initializing the adapter
listView.setAdapter(adapter);
UIUtils.setListViewHeightBasedOnItems(listView);

//whenever the data changes
adapter.notifyDataSetChanged();
UIUtils.setListViewHeightBasedOnItems(listView);
35
HussoM

J'avais une ListView dans ma mise en page et je voulais utiliser une bibliothèque qui ne peut pas gérer une ListView ici car elle est encapsulée dans une ScrollView. La meilleure solution pour moi est basée sur la réponse de Fedor.

Puisque j'ai déjà une ArrayAdapter pour la ListView je voulais le réutiliser:

LinearLayout listViewReplacement = (LinearLayout) findViewById(R.id.listViewReplacement);
NamesRowItemAdapter adapter = new NamesRowItemAdapter(this, namesInList);
for (int i = 0; i < adapter.getCount(); i++) {
    View view = adapter.getView(i, null, listViewReplacement);
    listViewReplacement.addView(view);
}

Pour moi, cela fonctionne bien car il me suffit d'afficher des données dynamiques variant de 1 à 5 éléments. Je devais juste ajouter mon propre diviseur.

11
Kai

Vous pouvez créer votre propre customlistview. (Il peut étendre ListView/ExpandableListView/GridView) et écraser la méthode onMeasure avec ceci Avec cela, vous n'aurez jamais besoin d'appeler une fonction ou quoi que ce soit. Il suffit de l'utiliser dans votre XML.

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
            MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}
9
Francisco

Si quelqu'un a toujours le problème, vous pouvez créer customList et ajouter la méthode onMesure() comme je l'ai implémenté:

public class ScrolleDisabledListView extends ListView {

private int mPosition;

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

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

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

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Record the position the list the touch landed on
        mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
        return super.dispatchTouchEvent(ev);
    }

    if (actionMasked == MotionEvent.ACTION_MOVE) {
        // Ignore move events
        return true;
    }

    if (actionMasked == MotionEvent.ACTION_UP) {
        // Check if we are still within the same view
        if (pointToPosition((int) ev.getX(), (int) ev.getY()) == mPosition) {
            super.dispatchTouchEvent(ev);
        } else {
            // Clear pressed state, cancel the action
            setPressed(false);
            invalidate();
            return true;
        }
    }

    return super.dispatchTouchEvent(ev);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
            MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}
}
3
Rojina

Regarde ça:

ListView en ignorant wrap_content

Sous Android: layout_height et Android: layout_weight l’a résolu pour moi:

<ListView
    Android:layout_height="0dp"
    Android:layout_weight="1"
    />
2
tinsukE

Je viens de le faire en utilisant les paramètres de réglage de ListView

public static void setListViewHeightBasedOnChildren(ListView listView) {

    //this comes from value from xml tag of each item
    final int HEIGHT_LARGE=75;
    final int HEIGHT_LARGE=50;
    final int HEIGHT_LARGE=35;
    ViewGroup.LayoutParams params = listView.getLayoutParams();

    int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;

    switch(screenSize) {

    case Configuration.SCREENLAYOUT_SIZE_LARGE:
         params.height =(int) (HEIGHT_LARGE*size);
         break;
    case Configuration.SCREENLAYOUT_SIZE_NORMAL:
         params.height =(int) (HEIGHT_NORMAL*size);
         break;
    case Configuration.SCREENLAYOUT_SIZE_SMALL:
          params.height =(int) (HEIGHT_SMALL*size);
          break;
    }
    listView.setLayoutParams(params);  
}
1
Ivan

Si vous souhaitez une solution simple à ce problème sans étendre la classe ListView, cette solution est pour vous.

 mListView.post(new Runnable() {
        @Override
        public void run() {
            int height = 0;
            for(int i = 0; i < mListView.getChildCount();i++)
                height += mListView.getChildAt(i).getHeight();
            ViewGroup.LayoutParams lParams = mListView.getLayoutParams();
            lParams.height = height;
            mListView.setLayoutParams(lParams);
        }
    });
0
Kvant

Je n'ai pas d'en-tête statique, mais en utilisant le message de HussoM comme indice, voici ce que j'ai pu me rendre au travail. Dans mon scénario, la hauteur des éléments de la liste était non uniforme, en raison de phrases de texte variables dans chacun des éléments. J'utilise wrap_content pour la hauteur et match_parent pour la largeur.

public class NonScrollableListView extends ListView {

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

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

  public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
  }

  public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }

  /**
   * Measure the height of all the items in the list and set that to be the height of this
   * view, so it appears as full size and doesn't need to scroll.
   * @param widthMeasureSpec
   * @param heightMeasureSpec
   */
  @Override
  public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      ListAdapter adapter = this.getAdapter();
      if (adapter == null) {
          // we don't have an adapter yet, so probably initializing.
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          return;
      }

      int totalHeight = 0;

      // compute the height of all the items
      int itemCount = adapter.getCount();
      for (int index=0; index<itemCount; index++) {
          View item = adapter.getView(index, null, this);
          // set the width so it can figure out the height
          item.measure(widthMeasureSpec, 0);
          totalHeight += item.getMeasuredHeight();
      }

      // add any dividers to the height
      if (this.getDividerHeight() > 0) {
          totalHeight += this.getDividerHeight() * Math.max(0, itemCount - 1);
      }

      // make it so
      this.setMeasuredDimension(widthMeasureSpec,
              MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY));
  }

}

0
cmarcelk

Je ne crois pas que personne puisse voir cela. On ne peut pas avoir deux parchemins sur la même présentation. D'abord, vous avez un scrollview et ensuite vous avez une liste, je parie que vous tuez certaines bonnes pratiques Android.

0
Catluc

Si tous les éléments ont la même hauteur

        int totalItemsHeight = baseDictionaries.size() * item.getMeasuredHeight();
        int totalDividersHeight = listView.getDividerHeight() * (baseDictionaries.size() - 1);
        int totalPadding = listView.getPaddingBottom() + listView.getPaddingTop();

        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) listTranslationWords.getLayoutParams();
        lp.height = totalItemsHeight + totalDividersHeight + totalPadding;
        listTranslationWords.setLayoutParams(lp);
0
Vahe Gharibyan