Maintain/Save/Restore Scroll Position When Returning to a Listview

Maintain/Save/Restore scroll position when returning to a ListView

Try this:

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());

// ...

// restore index and position
mList.setSelectionFromTop(index, top);

Explanation:


ListView.getFirstVisiblePosition() returns the top visible list item. But this item may be partially scrolled out of view, and if you want to restore the exact scroll position of the list you need to get this offset. So ListView.getChildAt(0) returns the View for the top list item, and then View.getTop() - mList.getPaddingTop() returns its relative offset from the top of the ListView. Then, to restore the ListView's scroll position, we call ListView.setSelectionFromTop() with the index of the item we want and an offset to position its top edge from the top of the ListView.

How to retain Android ListView scroll position when returning to an activity?

Save the position in an instance variable on onPause and in onResume check if there is position value and move to it.

e.g. using

private int currentPostion;

and then in onResume do..

getListView().setSelection(currentPosition);

Restore items and scroll position in listview on orientation change

To an unrelated degree, you should probably be using RecyclerView instead of ListView, which nowadays also supports a custom state restoration policy so that the scroll state is automatically preserved until data is set.

Anyways, following current approach, should be as simple as

@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
ArrayList<Song.Hits> values = savedInstanceState.getParcelableArrayList("list");

should be

@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
values = savedInstanceState.getParcelableArrayList("list");

How to Save and Restore the Scroll Position and State of ListView

Introduce a structure like below :

/**
* Introduce a class with below attributes to hold a state of each row in single
* element
*
*/
public class MyData {
/* Image url or path of image in single row */
private String images;

/* anme of image in single row */
private String name;

/* status ID of image in single row */
private String statusID;

/* message of image in single row */
private String message;

// Generate getters and setter
public String getImages() {
return images;
}

public void setImages(String images) {
this.images = images;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getStatusID() {
return statusID;
}

public void setStatusID(String statusID) {
this.statusID = statusID;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

}

Added appropriate comments to understand the code.

// MyDataList is the arrylist ArrayList(), you need to init this data structure in constructor

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

MyData fields = MyDataList.get(position);
}

Edit :

I have edite the code above posted , Look at code below how i used The MyData to set the status

package com.example.trial;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {

static ListView lstView;
private Handler handler = new Handler();;
static List<MyData> ImageList;
String strPath;
int position;
File newFile;
ViewHolder holder;
View v;
String fileName;
ImageAdapter mAdapter;

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

setContentView(R.layout.activity_main);
/*** Get Images from SDCard ***/
ImageList = getSD();
// ListView and imageAdapter
lstView = (ListView) findViewById(R.id.listView);
mAdapter = new ImageAdapter(this);
lstView.setAdapter(mAdapter);

}

private List<MyData> getSD() {
List<MyData> it = new ArrayList<MyData>();
String root_sd = Environment.getExternalStorageDirectory().toString();
File f = new File(root_sd + "/Download");
File[] files = f.listFiles();
for (int i = 0; i < files.length; i++) {
File file = files[i];
Log.d("Count", file.getPath());
MyData data = new MyData();
data.setImages(file.getPath());
data.setStatusEnable(true);
it.add(data);
}
return it;
}

static class ViewHolder {
public ViewHolder(View convertView) {
// TODO Auto-generated constructor stub
}

TextView textName;
ImageView thumbnail;
TextView textStatus;
Button btnUpload;

}

public class ImageAdapter extends BaseAdapter {
public ImageAdapter(Context c) {

}

public int getCount() {
// TODO Auto-generated method stub
return ImageList.size();
}

public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}

public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

public View getView(final int position, View convertView,
ViewGroup parent) {
// Avoid unneccessary calls to findViewById() on each row, which is
// expensive!

holder = null;

if (convertView == null) {
convertView = getLayoutInflater().inflate(
R.layout.adapter_main, null);
holder = new ViewHolder(convertView);

// Create a ViewHolder and store references to the children
// views
holder.textName = (TextView) convertView
.findViewById(R.id.textName);
holder.thumbnail = (ImageView) convertView
.findViewById(R.id.thumbnail);
holder.btnUpload = (Button) convertView
.findViewById(R.id.btnUpload);
holder.textStatus = (TextView) convertView
.findViewById(R.id.textStatus);

// The tag can be any Object, this just happens to be the
// ViewHolder
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.btnUpload.setEnabled(ImageList.get(position)
.isStatusEnable());
holder.textStatus.setText(ImageList.get(position).getMessage());
strPath = ImageList.get(position).getImages().toString();

// Get File Name
fileName = strPath.substring(strPath.lastIndexOf('/') + 1,
strPath.length());
File file = new File(strPath);
@SuppressWarnings("unused")
long length = file.length();
holder.textName.setText(fileName);

final BitmapFactory.Options options = new BitmapFactory.Options();

Bitmap bm = BitmapFactory.decodeFile(strPath, options);
holder.thumbnail.setImageBitmap(bm);

// btnUpload
holder.btnUpload.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Upload
startUpload(position);
}
});

return convertView;

}
}

// Upload

public void startUpload(final int position) {

Runnable runnable = new Runnable() {

public void run() {

handler.post(new Runnable() {

public void run() {
v = lstView.getChildAt(position
- lstView.getFirstVisiblePosition());
holder = (ViewHolder) v.getTag();
synchronized (this) {
ImageList.get(position).setStatusEnable(false);
mAdapter.notifyDataSetChanged();
}

new UploadFileAsync().execute(String.valueOf(position));
}
});
}
};
new Thread(runnable).start();
}

// Async Upload
public class UploadFileAsync extends AsyncTask<String, Void, Void> {

String resServer;

protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected Void doInBackground(String... params) {
position = Integer.parseInt(params[0]);
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1 * 1024 * 1024;
int resCode = 0;
String resMessage = "";

String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";

// File Path
String strSDPath = ImageList.get(position).getImages().toString();

// Upload to PHP Script
String strUrlServer = "http://mymasterpeice.comxa.com/uploadFile.php";

try {
/** Check file on SD Card ***/
File file = new File(strSDPath);
if (!file.exists()) {
resServer = "{\"StatusID\":\"0\",\"Message\":\"Please check path on SD Card\"}";
return null;
}

FileInputStream fileInputStream = new FileInputStream(new File(
strSDPath));

URL url = new URL(strUrlServer);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");

conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type",
"multipart/form-data;boundary=" + boundary);

DataOutputStream outputStream = new DataOutputStream(
conn.getOutputStream());
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
outputStream
.writeBytes("Content-Disposition: form-data; name=\"filUpload\";filename=\""
+ strSDPath + "\"" + lineEnd);
outputStream.writeBytes(lineEnd);

bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];

// Read file
bytesRead = fileInputStream.read(buffer, 0, bufferSize);

while (bytesRead > 0) {
outputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}

outputStream.writeBytes(lineEnd);
outputStream.writeBytes(twoHyphens + boundary + twoHyphens
+ lineEnd);

// Response Code and Message
resCode = conn.getResponseCode();
if (resCode == HttpURLConnection.HTTP_OK) {
InputStream is = conn.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();

int read = 0;
while ((read = is.read()) != -1) {
bos.write(read);
}

byte[] result = bos.toByteArray();
bos.close();

resMessage = new String(result);

}

Log.d("resCode=", Integer.toString(resCode));
Log.d("resMessage=", resMessage.toString());

fileInputStream.close();
outputStream.flush();
outputStream.close();

resServer = resMessage.toString();

} catch (Exception ex) {
ex.printStackTrace();
}

return null;
}

protected void onPostExecute(Void unused) {
statusWhenFinish(position, resServer);
}

}

// When Upload Finish
@SuppressWarnings("unused")
protected void statusWhenFinish(int position, String resServer) {

/*** Default Value ***/
String strStatusID = "0";
String strError = "";

try {

JSONObject c = new JSONObject(resServer);
strStatusID = c.getString("StatusID");
strError = c.getString("Message");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

// // prepare Status
if (strStatusID.equals("0")) {
// When update Failed
ImageList.get(position).setMessage("Failed");
ImageList.get(position).setStatusEnable(true);
mAdapter.notifyDataSetChanged();
} else {
ImageList.get(position).setMessage("Uploded");
ImageList.get(position).setStatusEnable(false);
mAdapter.notifyDataSetChanged();
}

}

/**
* Introduce a class with below attributes to hold a state of each row in
* single element
*
*/
public class MyData {
/* Image url or path of image in single row */
private String images;

/* anme of image in single row */
private String name;

/* status ID of image in single row */
private String statusID;

/* message of image in single row */
private String message;

private boolean statusEnable;

public boolean isStatusEnable() {
return statusEnable;
}

public void setStatusEnable(boolean statusEnable) {
this.statusEnable = statusEnable;
}

// Generate getters and setter
public String getImages() {
return images;
}

public void setImages(String images) {
this.images = images;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getStatusID() {
return statusID;
}

public void setStatusID(String statusID) {
this.statusID = statusID;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

}

}

How can I save the scroll position for my ListView that uses a CursorAdapter?

It's possible to use savedInstanceState to save the exact scroll of the ListView however, this is destroyed along with the activity under many circumstances and we can't easily persist the savedInstanceState as we can only really convert it into a Parcelable object, which isn't designed to be persisted. Even when trying to get the savedInstanceState from the ListView we have a few problems. null is often given back to us as the Android documentation specifies happens when there's "nothing interesting to save". Well, in this scenario, the scroll is pretty interesting! Sadly, Android isn't always going to give us this though, which brings a lot of problems...

In fact, I looked this up a bit more and noticed the Android documentation says "This state should only contain information that is not persistent or can not be reconstructed later. For example, you will never store your current position on screen because that will be computed again when a new instance of the view is placed in its view hierarchy.", so this is not the way to go.

Of course, we can still use the method you're using however, it will never be exact across all devices. For a general app, I would recommend just persisting the index of the list item and starting the user back at the top.

I don't believe there are any other methods we can use to access the scroll position, apart from getting the details from the ListView itself. Therefore, we can only use what we're given.

You could be super crazy mind, and try out putting a ListView in a ScrollView (Or some other containers would work too), where we can then use methods like getScrollY() and setScrollY() but this relies on there always being the same number of list items and all list items staying the same size.



Related Topics



Leave a reply



Submit