Home » Android » android – Keeping the last visible item of a ListView when the size of the ListView changes

android – Keeping the last visible item of a ListView when the size of the ListView changes

Posted by: admin April 23, 2020 Leave a comment

Questions:

What I want is simple, well at least I thought it would be simple.
I just want a window where an EditText is on the bottom of the screen, and the rest of the space is filled with ListView.
Unfortunately, it did not work as I expected. What I want is the following image. Is there any easy way to do this in the XML, or should I write some special code for this?
What I want

My Problematic Android Source Code.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
    android:id="@+id/demolist"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
     >
</ListView>
    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:layout_weight="0">
    </EditText>
</LinearLayout >
How to&Answers:

What you Want

Last Visible Item Without Keyboard

Last Visible Item Without Keyboard

Last Visible Item With Keyboard

Last Visible Item With Keyboard

Last Visible Item With larger input

Last Visible Item With larger input

Here is how i did it.

Summary

  • Set android:stackFromBottom=”true” and android:transcriptMode=”alwaysScroll” in your list view
  • Set android:windowSoftInputMode=”adjustPan” in Activity
  • Call adapter.notifyDataSetChanged(); when you add new chats so that the list view will always scroll to the last position
  • Set android:inputType=”textMultiLine” & android:maxLines=”4″ in EditText for growing text box

Details

Activity

<activity android:name="MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan"></activity>

Chat View

<RelativeLayout
    android:id="@+id/chat_header"
    android:layout_width="fill_parent"
    android:layout_height="40dp"
    android:padding="3dp" >

    <Button
        android:id="@+id/btnBackChat"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_alignParentLeft="true"
        android:background="@drawable/back_button" />

    <TextView
        android:id="@+id/txt_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/btnBackChat"
        android:layout_centerHorizontal="true"
        android:textColor="@android:color/white"
        android:textSize="18sp" />

    <ImageView
        android:id="@+id/imgViewProfileChat"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_alignParentRight="true"
        />
</RelativeLayout>

<ListView
    android:id="@+id/listView1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@+id/form"
    android:layout_below="@+id/chat_header"
    android:stackFromBottom="true"
    android:transcriptMode="alwaysScroll"/>

<RelativeLayout
    android:id="@+id/form"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:orientation="vertical" >

    <ImageButton
        android:id="@+id/btnAttach"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/attach"
        android:src="@drawable/attach_icon" />

    <EditText
        android:id="@+id/txtChat"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/btnSend"
        android:layout_toRightOf="@+id/btnAttach"
        android:inputType="textMultiLine"
        android:maxLines="4" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="@string/send" />
</RelativeLayout>

Answer:

I think you may want to use AdapterView.getLastVisiblePosition() before displaying the keyboard.

Two solutions here:

  • on EditText click
  • thanks to an OnScrollListener (and its onScroll function) listening to your ListView

then use the AdapterView.setSelection to jump to this item (or index – 2 or index – 3) after EditText focus.

Answer:

You can use android:windowSoftInputMode="adjustPan" in your AndroidManifest.xml as below

 <activity android:name="com.example.SimpleListView"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustPan" >

This should produce the output you want. Official android documentation .

Optionally you can specify transcript mode(if you are working on some chat feature) for your list view if you want to scroll automatically to the end of listview. You can check the android:transcriptMode="normal" as well and see if that fits.

A related SO answer. Official android doc.

<ListView
android:id="@+id/comments"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transcriptMode="alwaysScroll">

This should solve most of your issues, maybe except for the last one(in the image) which I haven’t tested.

Answer:

Just override the method of ListView#onSizeChanged(int w, int h, int oldw, int oldh) like below:
PS:Your ListView’s height can not be 0 anytime,if it may 0,not working.

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (getChildCount() == 0) {
        return;
    }
    final int lastVisiblePosition = getLastVisiblePosition();
    int firstVisiblePosition = getFirstVisiblePosition();

    View last = getChildAt(lastVisiblePosition - firstVisiblePosition);
    int top = last.getTop();
    final int newTop = top + (h - oldh);
    Log.d(getClass().getSimpleName(), "onSizeChanged(" + w + ", " + h + ", " + oldw + ", " + oldh + ")");
    if (mFocusSelector == null) {
        mFocusSelector = new FocusSelector();
    }
    post(mFocusSelector.setup(lastVisiblePosition, newTop));
    super.onSizeChanged(w, h, oldw, oldh);
}

private class FocusSelector implements Runnable {
    private int mPosition;
    private int mPositionTop;

    public FocusSelector setup(int position, int top) {
        mPosition = position;
        mPositionTop = top;
        return this;
    }

    public void run() {
        setSelectionFromTop(mPosition, mPositionTop);
    }
}

Answer:

I actually got the same behavior that you need by adding to Activity declaration in manifest android:windowSoftInputMode="adjustResize" and to the ListView declaration android:transcriptMode="normal". For both keyboard and orientation changes.

Answer:

I need this functionality too. Here’s my attempt, but it doesn’t quite work — The scrolling is a bit erratic (doesn’t always end up where it should) and it interferes with the preservation of the scroll position when rotating the screen.

@Override
public void onResume() {
    super.onResume();
    getListView().addOnLayoutChangeListener(stickyLastItemHandler);
}

@Override
public void onPause() {
    super.onPause();
    getListView().removeOnLayoutChangeListener(stickyLastItemHandler);
}

View.OnLayoutChangeListener stickyLastItemHandler = new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v,
            int left, int top, int right, int bottom,
            int oldLeft, int oldTop, int oldRight, int oldBottom) {

        final int heightChange = (bottom - top) - (oldBottom - oldTop);
        if (heightChange != 0) {
            ((AbsListView)v).smoothScrollBy(-heightChange, 1000);
        }
    }
};

Answer:

You can do it through xml:

<ListView
    android:stackFromBottom="true"
    android:layout_height=0dp
    android:layout_weight=1 />

Stacking from bottom will keep ListView scrolled down by default,
setting ListView’s weight will fill all possible space within parent vertical LinearLayout.

Answer:

I created a layout engine for Android that allows you to listen for keyboard hidden and visible state changes (among many other core features). This library is available at http://phil-brown.github.io/AbLE/.

Simply add the jar to your libs directory (create this folder if it does not exist), and instead of inheriting from an Activity, inherit from AbLEActivity. Next, Override the methods onKeyboardShown and onKeyboardHidden, and in these you can animate your content view up or down as needed:

@Override
public void onKeyboardShown() {
    //animate up
}

@Override
public void onKeyboardHidden() {
    //animate down
}

Answer:

Follow these steps
--------------------
1. Update your activity in AndroidManifest.xml file like below
<activity
            android:name=".MyListActivity"
            android:windowSoftInputMode="adjustResize" />

2. Set your list view with like below.
mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
mListView.setStackFromBottom(true);

I hope it will solve your problem.

Answer:

Supposing you know when the list data has changed, you can manually tell the list to scroll to the bottom by setting the list selection to the last row. Something like this:

private void scrollMyListViewToBottom() {
    myListView.post(new Runnable() {
        @Override
        public void run() {
            // Select the last row so it will scroll into view...
            myListView.setSelection(myListAdapter.getCount() - 1) ;
        }
    });
}

Or you can also try this:

ChatAdapter adapter = new ChatAdapter(this);

ListView lview = (ListView) findViewById(R.id.chatList);
lview .setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
lview .setAdapter(adapter);

adapter.registerDataSetObserver(new DataSetObserver() {
    @Override
    public void onChanged() {
        super.onChanged();
        lv.setSelection(adapter.getCount() - 1);    
    }
});

Answer:

In your ListAdapter, you could do something like this :

   public View getView(int position, View convertView, ViewGroup parent) {

//buttonMore
if( position >= super.getCount()  )
      return textField ;

    ....

This way, you will always get your last item in the list as your textfield.

Answer:

You need to set selection position… But – in a strange way, i don’t really understand why – you need to do it with post()

public void ScrollToLast() {
    yourList.clearFocus();
    yourList.post(new Runnable() {
        @Override
        public void run() {
            yourList.setSelection(items.size() - 1);
        }
    });
}

So you can add a TextWatcher for your editText and you can call this method there… So your list will scroll to bottom.