Редовете на Android ListView в ScrollView не се показват напълно - изрязани

Срещнах проблем при вграждане на ListView в ScrollView, или поне оттам предполагам, че идва проблемът. Елементът ListView е доста прост:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/general_background_list_middle" 
    android:paddingTop="4dp"
    android:paddingBottom="4dp"
    android:paddingRight="0dp"
    android:paddingLeft="0dp">

    <ImageView
        android:id="@+id/chat_friends_avatar"       
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_marginLeft="7dp"
        android:layout_marginRight="8dp"
        android:paddingRight="0dp" 
        android:paddingLeft="0dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/friends_icon_avatar_default"/>

    <TextView
        android:id="@+id/chat_message_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/chat_friends_avatar"
        android:layout_alignParentTop="true"
        android:layout_marginRight="35dp"
        android:maxLines="10"
        android:textSize="12dp"/>

    <TextView
        android:id="@+id/chat_friend_name"
        android:layout_width="140dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="3dp"
        style="@style/SubText"
        android:layout_toRightOf="@id/chat_friends_avatar"      
        android:layout_below="@id/chat_message_text" />

    <TextView
        android:id="@+id/chat_message_time"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        android:layout_marginTop="3dp"
        style="@style/SubText"
        android:layout_alignParentRight="true"
        android:layout_below="@id/chat_message_text" />

</RelativeLayout>   

Въпреки това, когато вградя списък с такива елементи в ScrollView, между някои други елементи, редовете не се показват напълно, те се изрязват (вижте изображението по-долу), ако текстът е обвит. ListView се инстанцира, както следва в ScrollView:

<ListView
android:id="@+id/info_chat_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:cacheColorHint="@color/frame_background_color"
android:clickable="false"
android:divider="@null"
android:footerDividersEnabled="false"
android:focusable="false" >
</ListView> 

Ако височината на ListView е зададена на "wrap_content", се показва само първият елемент. Ето защо използвам метод за изчисляване на височината на редовете на списъка:

private int getCommentsListHeight() {
        if (mChatAdapter != null && mChatAdapter.getCount() != 0) {
            if (mChatList != null) {// && mCommentsListItemHeight == 0) {
                mCommentsListItemHeight = 0;
                for (int i = 0; i < mChatAdapter.getCount(); i++) {
                    // Get view item height
                    View viewItem = mChatAdapter
                            .getView(i, new View(OnAirActivity.this), mChatList);
                    viewItem.measure(0, 0);
                    Logger.d(LOGTAG, "View " + i + " measured height = " + viewItem.getMeasuredHeight());
                    mCommentsListItemHeight += viewItem.getMeasuredHeight();
                }
            }
            //return mChatAdapter.getCount() * mCommentsListItemHeight;
            return mCommentsListItemHeight;
        } else {
            return 0;
        }
    }

За съжаление, в случай, че текстът в TextView е обвит, дори в няколко реда, височината на елемента на реда, върнат от метода getMeasuredHeight(), е постоянна. Също така getLineCount(), извикан на TextView вътре в елемента на реда, връща 1 дори ако текстът е обвит.

От друга страна, ако този ListView е вграден в LinearLayout, всичко работи добре и пълният списък се показва без изрязване.

Имате ли някакви предложения какво може да не е наред тук? Наистина не ми харесва идеята за ръчно измерване на височината на елементите на списъка и очевидно не работи, но защо Android не може да разтегне добре ListView вътре в ScrollView, за да го побере всичко там?

Изрязан списък: вижте изображението


person PawelPredki    schedule 22.05.2012    source източник


Отговори (10)


ЛОША практика е да капсулирате ListView в ScrollView, защото самият ListView съдържа възможности за превъртане. Трябва да приложите решение, което не съдържа такава йерархия от изгледи и се надявам, че ще направи магията :)

person waqaslam    schedule 22.05.2012
comment
Благодаря за бързия отговор. Така че вашето предложение би било, например, да създадете тези изгледи на редове програмно вътре в Scrollview един по един? - person PawelPredki; 23.05.2012
comment
Не. Трябва да използвате ListView, но не го поставяйте в ScrollView. Можете да персонализирате елементи от редове, като замените метода getView от адаптера, използван за ListView - person waqaslam; 23.05.2012
comment
Използвам адаптер и това работи добре в тази друга дейност, която споменах, където ListView е в рамките на LinearLayout. Трябва да използвам ScrollView тук, тъй като моята активност съдържа много елементи, които не се побират на екрана, включително ListView за чат, който е източникът на всички проблеми... - person PawelPredki; 23.05.2012
comment
тогава може би бих ви предложил да използвате горните и/или долните колонтитули на ListView, за да зададете всички други изгледи, които имате в момента в ScrollView. - person waqaslam; 23.05.2012
comment
Това е малко пресилено, тъй като по-голямата част от елементите в използвания в момента ScrollView НЕ са списъкът, който е обезпокоителен... Разбирам, че няма начин ListView да работи добре в ScrollView, така че трябва да помисля кои заобиколно решение/решение е най-доброто в моя случай. Благодаря за помощта! - person PawelPredki; 23.05.2012

Използвайте този метод, създаден от https://stackoverflow.com/users/205192/dougw

 public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter(); 
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = 0;
        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            listItem.measure(0, 0);
            totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }
person Rajesh Wadhwa    schedule 19.04.2013
comment
Това работи за мен, но нямам проблем с обвиването на текста, както беше в първоначалния въпрос. Благодаря! - person Keith Entzeroth; 07.02.2014
comment
Това работи добре, но след това, когато добавих подложка около списъчния изглед, за да премахна текста от ръба на екрана, малък къс отново се изрязва. Какво трябва да добавя, за да поправя това? - person cnfw; 20.07.2014
comment
params.height е крайната височина, която е зададена. Можете да добавите фиксиран буфер, в случай че трябва да продължите да допълвате - person Rajesh Wadhwa; 21.07.2014
comment
Как да внедрим зареждане на повече данни? - person Krunal Shah; 17.01.2015
comment
Този отговор е копиран почти дословно от тук: stackoverflow.com/a/3495908/1221537 - person adamdport; 10.03.2015
comment
Това е лошо решение! Това ще създаде много безполезни изгледи. Самата идея е добра, но не по този начин. - person rekire; 10.07.2016
comment
Може да показва елемент от списъка, но изгледът на долния колонтитул не може да се покаже, когато добавих с Listview.addfooter(View). Всъщност оформлението на долния колонтитул съществува. Как да направя така, че да виждам футер изглед? - person james; 27.07.2016
comment
@Denny всяко извикване на listAdapter.getView(i, null, listView) ще създаде нов изглед и само за измерването. Това е много лошо за представянето. - person rekire; 18.09.2018
comment
Това наистина ли създава нови изгледи или просто получава това, което вече е там? Струва ми се второто - person Denny; 19.09.2018

Ето ресурс за основно оформление със ScrollView:

<ScrollView android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/parentLayout"/>
</ScrollView>

Ето кода за вмъкване на елементи:

parentLayout.removeAllViews();
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = comments.size() - 1; i >= 0; i--) {
CommentInfo comment = comments.get(i);
View view = inflater.inflate(your_resource_id, null, false);

TextView commentsContent =(TextView)view.findViewById(R.id.commentContent);
if (commentsContent != null) {
    String data = String.format("%s (by %s, %s)",  comment.getCommentText(), comment.getUserName(),
        commentsContent.setTextSize(st.getTextSize());
    commentsContent.setText(data);

}

parentLayout.addView(view, 0);

}       
person Layko Andrey    schedule 24.05.2012
comment
Това би било добро решение, ако единствените елементи в моя ScrollView бяха изгледът с коментари. И в този случай поставянето на ListView в LinearLayout би било най-доброто решение. В моя случай има много повече елементи в LinearLayout в ScrollView, така че първо трябва да знам къде да поставя коментарите. Предполагам, че и двата подхода са добри. - person PawelPredki; 24.05.2012
comment
Няма проблеми за добавяне на всеки елемент в ScrollView след и преди LinearLayout. Просто изрязах някакъв код, за да покажа точно, от което се нуждаете. В реалния файл имам много контроли преди LinearLaout и след включването на още един LinearLayout, който също се използва като ListView. - person Layko Andrey; 24.05.2012
comment
Ако имате други дъщерни елементи в ScrollView с изключение на LinearLayout, тогава това е против това, което Android ви казва да направите тук: developer.android.com/reference/android/widget/ScrollView.html ScrollView е FrameLayout, което означава, че трябва да поставите едно дете в него, съдържащо цялото съдържание за превъртане; самото това дете може да бъде мениджър на оформление със сложна йерархия от обекти - person PawelPredki; 24.05.2012
comment
Добавете горно оформление, например LinearLayout, като дъщерен ScrollView и добавете към него дъщерно оформление всякакви други контроли, включително LinearLayouts, които биха се използвали като контейнер на списък. Аз правя същото, работи правилно. - person Layko Andrey; 24.05.2012
comment
какво, ако се изисква ExpandableListView вместо ListView? - person CoDe; 07.01.2015

Имах същия проблем в моя проект. Трябва да създадете обикновен LinearLayout в ScrollView. След това трябва да създадете нов изглед с вашия xml елемент за списък с помощта на LayoutInflater. След създаването поставете всички данни в нов изглед и добавете към LinearLayout като дъщерен изглед:

linearLayot.addView(newView, position_you_need).

Надявам се, че ще ви помогне!

person Layko Andrey    schedule 23.05.2012
comment
Така че вие ​​предлагате да поставите оформленията в следния ред: <ScrollView> <LinearLayout> <... my other views ...> <LinearLayout> <ProblematicListView> </LinearLayout> <... more views ...> </LinearLayout> </ScrollView> Това не е ли почти същото? ListView все още е вътре в ScrollView? Точно сега избрах „ръчния“ начин, като направих ‹include android:id=@+id/chat_comment_x layout=mylayout›, x=0..5 И имам масив от контейнери за тези пет оформления, които попълвам с помощта на същия адаптер, който използвах за изгледа на моя списък. Всъщност имам динамичен списък от 5 елемента и той работи добре. - person PawelPredki; 23.05.2012
comment
Не, предлагам ви да направите следното: ‹ScrollView› ‹LinearLayout› ‹... другите ми изгледи ...› ‹LinearLayout›‹/LinearLayout› ‹/ScrollView›. Основната идея е да поставите елементите от списъка си в линейно оформление, а не в специално оформление на списъчен изглед. Всички елементи ще бъдат превъртани и показани без изрязване. - person Layko Andrey; 24.05.2012

Приех присърце препоръката да не използвам ListView елемент в ScrollView и реших да използвам леко груб метод, за да постигна това, от което се нуждая. Тъй като има постоянен брой до пет реда от списък, които трябва да бъдат показани, аз премахнах екземпляра на ListView от xml файла и го замених с пет екземпляра на редове:

<include android:id="@+id/info_comment_1" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_2" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_3" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_4" layout="@layout/chat_single_message" />
<include android:id="@+id/info_comment_5" layout="@layout/chat_single_message" />

В класа Activity декларирам пет контейнера за тези изгледи:

private RelativeLayout mChatMessages[] = new RelativeLayout[COMMENTS_NUMBER];

и ги инициализирайте с:

mChatMessages[0] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_1);
mChatMessages[1] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_2);
mChatMessages[2] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_3);
mChatMessages[3] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_4);
mChatMessages[4] = (RelativeLayout) mMoreInfoLayout.findViewById(R.id.info_comment_5);

След това, когато се получи ново съобщение, използвам ChatAdapter (същия, който използвах за ListView преди) и извиквам неговия метод getView():

protected void updateChatMessages() {
  int msgCount = mChatAdapter.getCount();
  for (int i = 0; i < COMMENTS_NUMBER; i++) {
    if (msgCount <= i) {
      mChatMessages[i].setVisibility(View.GONE);
    } else {
      mChatMessages[i] = (RelativeLayout) mChatAdapter.getView(i, mChatMessages[i], null); 
      mChatMessages[i].setVisibility(View.VISIBLE);
    }   
  }
}

Никога повече не увеличавам изгледите pariculat, тъй като единственото нещо, което се променя, е съдържанието на всеки ред, а не оформлението. Това означава, че тук няма наказание за изпълнение.

Това е основно ръчна реализация на ListView с ограничен максимален брой елементи. Този път обаче ScrollView успява да ги напасне добре и нищо не се изрязва.

За динамичен брой редове може да се използва подходът, предложен от Layko, като изгледите се инстанциират програмно и се добавят към LinearLayout вътре в ScrollView.

person PawelPredki    schedule 23.05.2012

Виждам, че ListView е вътре в ViewPager; друг прост подход за разрешаване на този проблем е да добавите app:layout_behavior="@string/appbar_scrolling_view_behavior" към ViewPager във вашия xml оформление, както се вижда по-долу.

<android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

За да предотвратите същото поведение в долната част на списъка, можете също да добавите android:layout_marginBottom="?attr/actionBarSize" към ViewPager по този начин

<android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_marginBottom="?attr/actionBarSize"/>

Това идва със закъснение, но се надявам да помогне на някой друг.

person Cletus Ajibade    schedule 23.03.2017

опитайте.. след създаване на целия изглед добавете реда по-долу за местоположението на ScrollView на екрана (x,y)

  ScrollView scrollView = (ScrollView)findViewById(R.id.scrollView);

  scrollView.smoothScrollTo(0,0);// top location zero index
person Neeraj Singh    schedule 08.05.2017

Имах подобен проблем. имам

RelativeLayout
  listView
  includeLayout  

където включвам някаква долна навигация под listView с това

<include
    android:id="@+id/includeLayout"
    layout="@layout/bottom_nav_bar"

и моят listView беше изрязан - не заема пълната налична височина между заглавката и долната навигация. Опитах различни xml настройки, предложени в тази и други теми, но това, което ми помогна, беше да добавя

android:layout_alignBottom="@+id/includeLayout"

към моя listView. Това изглежда издърпа listView надолу до горната част на долната навигация, така че listView сега използва пълната налична височина (и превърта според нуждите).

person John Leasia    schedule 10.05.2017

Това работи за мен

<RelativeLayout                                          xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:padding="@dimen/activity_vertical_margin">

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="17sp"
        android:text="@string/text_list_devices" />

    <ListView
        android:id="@+id/lv_paired"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:cacheColorHint="#00000000"
        android:layout_above="@+id/signup_t"
        android:layout_below="@id/tv_status"
        />

    <Button

        android:id="@+id/signup_t"
        style="?android:textAppearanceSmall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:textColor="@android:color/white"
        android:layout_alignParentBottom="true"
        android:text="Print All Records"
        android:typeface="sans"
        android:layout_marginLeft="45dp"
        android:layout_marginRight="45dp"
        android:textSize="24sp"
        android:background="@drawable/selector_for_button"
        android:textStyle="bold" />
</RelativeLayout>
person Nouman Ch    schedule 16.11.2017

Това е ЛОША практика. Но има някои ситуации, в които не можем да избегнем използването им. Например динамични оформления за електронна търговия, ние може да поставим множество списъци или да рециклираме изгледи, но вие не искате да превъртате вътре в една височина на елемент (ако случайно искате!!). Сблъсках се с такъв проблем. Поправих по прост начин. Не казвам, че това е правилният начин, но може да помогне на някои. !! Преди рециклирах гледката.

(01) Създайте интерфейс за връщане на височината на изгледа.

public interface AfterViewLoadListener {

    void onViewHeightMeasured(int height, String mode);
}

(02) прилагайте с вашата дейност

public class *Activity extends AppCompatActivity implements AfterViewLoadListener{
  /**  your codes **/
  final SimpleListRecycleAdapter order_adapter = new SimpleListRecycleAdapter(this,"ORDER");
}

@Override
public void onViewHeightMeasured(int height, String mode) {
    if(mode.equals("ORDER") && height > 0){
        recycleView.setMinimumHeight(height);
    }
}

(03) вътре в персонализирания адаптер за рециклиране

AfterViewLoadListener viewLoadListener = null;
public SimpleListRecycleAdapter(AfterViewLoadListener listener, String mode) {
    if(listener instanceof AfterViewLoadListener){
        viewLoadListener = listener;
    }
    this.mode = mode;
}

(04) замени метода onViewAttachedToWindow

@Override
public void onViewAttachedToWindow(@NonNull SimpleListViewHolder holder) {
    super.onViewAttachedToWindow(holder);
    View view = holder.itemView;
    view.measure(0, 0);
    this.viewMinHeight = view.getMeasuredHeight();

    if(!firstFlag){
        firstFlag = true;
        viewLoadListener.onViewHeightMeasured(this.viewMinHeight*filtered.length(),mode);
    }
}

(05) Това е. При мен се получи.

person Hariharan Kanakaraja    schedule 18.03.2020