如何保存和恢复ListView的滚动位置和状态

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

让我们看看截图的工作,以便更好地理解问题:

  1. 我已经上传第一个列表项的图像到服务器[i.e:- Thumbnail 1.png],见下图:

  1. 我使用 Localhost:
  2. myfile 文件夹下上传了图片

  1. 当我 向下滚动 ListView 时,得到这个 - 真正的痛苦来了,如你所见 Thumbnail 7,我没有点击它:

  1. 当我再次 向上滚动 ListView 时,得到这个东西 - 这让我很恼火,以同样的方式 Thumbnail 2,我没有碰那:

我想that's enough向你解释,我想做什么发生了什么哪里 我遇到问题

现在轮到你告诉我解决方案了,我该如何解决这个问题?

一些解释:

我已经将第一个列表项的图片上传到服务器(根据我的要求,我得到的图片状态为 "uploaded" 并且上传按钮现在已禁用)但是当我滚动 down/up 列表视图时它显示列表中上传的任何项目不是我上传的项目(我主要注意到可见的列表项目)并重置最初上传到服务器的第一个列表项目的状态...

所以在这里我只想让我最初上传的那个特定的上传状态稳定...而不是任何可见的项目(那些还没有上传)

只影响列表视图,服务器没有任何反应(只得到一张我上传的图片)

MainActivity.java:-

public class MainActivity extends Activity  {

    static ListView lstView;
    private Handler handler = new Handler();;

    static List <String> ImageList;
    String strPath;     

    int position ;

    static File f;
    File newFile;


    static File[] files ;
    static File  file ;

    ViewHolder holder;
    View v ;        

    String fileName;    

    @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);
    lstView.setAdapter(new ImageAdapter(this));

    }

    private List <String> getSD()
    {
        List <String> it = new ArrayList <String>();
        File f = new File ("/mnt/sdcard/mydata/");
        File[] files = f.listFiles ();

        for (int i = 0; i <files.length; i++)
        {
            File  file = files[i];
            Log.d("Count",file.getPath());
            it.add (file.getPath());
        }
        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();             
            }

            strPath = ImageList.get(position).toString();

            // Get File Name
            fileName = strPath.substring( strPath.lastIndexOf('/')+1, strPath.length() );
            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();
                    holder.btnUpload.setEnabled(false);

                        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) {
                    // TODO Auto-generated method stub
                        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).toString();

                        // Upload to PHP Script
                        String strUrlServer = "http://10.0.2.2/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
                       holder.textStatus.setText("Failed");
                       holder.btnUpload.setEnabled(true);
                    }
                    else
                    {
                        holder.textStatus.setText("Uploaded");  
                        holder.btnUpload.setEnabled(false);
                    }

            }

}

你应该有一个 Upload Status 的数组,它将被传递给默认值为 false 的适配器,每次你在服务器上成功上传文件时,将状态更改为与该位置对应的数组项目清单。

例如

默认数组(假设列表的大小为 7)

{"false","false""false""false""false""false""false"}  

每成功上传一个文件,将Array的状态更改为

{"false","true""false""false""false""false""true"}

现在你只需要更新这个数组中的值并且必须调用

youradapter.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;

    // 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;
    }

}

添加了适当的注释以理解代码。

// MyDataList是arrylist ArrayList(),需要在构造函数中初始化这个数据结构

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

    MyData fields = MyDataList.get(position);
}

编辑:

我编辑了上面发布的代码,请看下面的代码我是如何使用 MyData 设置状态的

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;
        }

    }

}

创建您要使用的数据类型列表。例如。

class YourDataType{
    String name, status;
    Bitmap image;
}

然后使用列表来保存列表中的所有对象。

List<YourDataType> listOfObjects;

然后使用类似的参数创建适配器,并将对象列表传递给适配器的构造函数。

class ImagesAdapter extends BaseAdapter{
    public ImagesAdapter(Context c, List<YourDataType> listOfObjects){
        this.listOfObjects = listOfObjects;
    }

    public View getView(params, position){
        YourDataType data = listOfObjects.get(position);
        //Here you can use data to access specific object and upload the specific image and then mark this object's status to marked, then you can change the status of any listview item relevant to the object.
        if(data.status == uploaded)
             viewHolder.textStatus.setText("Uploaded");
    }
}

最好的做法是将适配器分开 希望能帮助到你。如果您有任何其他问题,请在下方评论。