从异步任务onRetainNonConfigurationInstance()获取ListView中的LinearLayout

时间:2012-05-18 08:35:19

标签: android android-listview

我希望这个头衔没有误导。我正在尝试实施onRetainNonConfigurationInstance()AsyncTask加载图片,并收到此错误"Java.lang.RuntimeException:Unable to retain Activity"。这是我的onRetainNonConfigurationInstance()代码:

public Object onRetainNonConfigurationInstance(){
  //final ListView listview = list;

  final int count = list.getChildCount();
    final LoadedImage[] mylist = new LoadedImage[count];

    for(int i = 0; i < count; i++){
        final ImageView v = (ImageView)list.getChildAt(i); // getting error here
        mylist[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
    }
    return mylist;
}

以下是Logcat

05-18 08:43:15.385: E/AndroidRuntime(28130): java.lang.RuntimeException: Unable to retain activity {com.MyApps.ImageGen/com.MyApps.ImageGen.Wallpapers}: java.lang.ClassCastException: android.widget.LinearLayout
05-18 08:43:15.385: E/AndroidRuntime(28130):    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:2989)
05-18 08:43:15.385: E/AndroidRuntime(28130):    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3100)
05-18 08:43:15.385: E/AndroidRuntime(28130):    at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3216)
05-18 08:43:15.385: E/AndroidRuntime(28130):    at android.app.ActivityThread.access$1600(ActivityThread.java:132)

以下是ListView的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

<ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:cacheColorHint="#00000000"
    android:listSelector="@android:color/transparent" >
</ListView>     
</LinearLayout>

以下是我设置ListView

的方法
private void setupViews() { 
        list = (ListView)findViewById(android.R.id.list);
        list.setDivider(null);
        list.setDividerHeight(0);
        imageAdapter = new ImageAdapter(getApplicationContext());
        list.setAdapter(imageAdapter);
        list.setOnItemClickListener(this);
}

我的异步任务和项目适配器:

class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {


    @Override
    protected Object doInBackground(Object... params) {

        Bitmap bitmap = null;
        Bitmap newbitmap = null;
        Uri uri = null;

        String [] img = {MediaStore.Images.Media._ID};

        String state = Environment.getExternalStorageState();

        if(Environment.MEDIA_MOUNTED.equals(state)){    

        cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, img, null, null, null);

        } else {
        // cursor = getContentResolver().query(MediaStore.Images.Media.INTERNAL_CONTENT_URI, img, null, null, null);

         inInternalStorage = true;
        }

        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
        int size = cursor.getCount();

        if(size == 0){
            //Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
            //System.out.println("size equals zero");

            Wallpaper.this.runOnUiThread(new Runnable() {
                public void run() {
                    Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
                }
            });

            return params;
        }else {

        }

        for(int i = 0; i < size; i++){
            cursor.moveToPosition(i);
            int ImageId = cursor.getInt(column_index);

            if(inInternalStorage == true){
             uri = Uri.withAppendedPath(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "" + ImageId);
         }else {
             uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + ImageId);
         }


         try {
             BitmapFactory.Options options = new BitmapFactory.Options();
             options.inJustDecodeBounds = true;

             BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);

             int imageHeight = options.outHeight;
             int imageWidth = options.outWidth;
             String imageType = options.outMimeType;

             Log.i(TAG, "imageheight = " + imageHeight);
             Log.i(TAG, "imagewidth = " + imageWidth);
             Log.i(TAG, "imageType = " + imageType);

             //options.inSampleSize=4;

             options.inSampleSize = calculateInSampleSize(options, imageWidth, imageHeight);

            options.inJustDecodeBounds = false;
             //bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
             bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);

             if(bitmap != null){
                 newbitmap = Bitmap.createScaledBitmap(bitmap, 180, 180, true);
                 bitmap.recycle();
             }
             if(newbitmap != null){
                 publishProgress(new LoadedImage(newbitmap));

             }
         }catch(IOException e){

         }
         //cursor.close();
        }
        cursor.close();
        return null;    
    }

    @Override
    public void onProgressUpdate(LoadedImage... value){
        addImage(value);
    }

       @Override
        protected void onPostExecute(Object result) {
            setProgressBarIndeterminateVisibility(false);
        }
}

private static class LoadedImage {
    Bitmap mBitmap;

    LoadedImage(Bitmap bitmap) {
        mBitmap = bitmap;
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }
}

/*Image Adapter to populate grid view of images*/

public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();


    public ImageAdapter(Context context){
        this.mContext = context;
    }

    public void addPhotos(LoadedImage photo){
        photos.add(photo);
    }

    @Override
    public int getCount() {
        return photos.size();
    }


    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

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

        ImageView image;
        ViewHolder holder;

        if(convertView == null){
            LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //convertView = inflater.inflate(R.layout.image_list,null);
            convertView = inflater.inflate(R.layout.media_view, null);
            holder = new ViewHolder();

            holder.image = (ImageView)convertView.findViewById(R.id.media_image_id);

            //holder.item_name = (TextView)convertView.findViewById(R.id.media_image_id);

            convertView.setTag(holder); 
        } else{
            holder = (ViewHolder)convertView.getTag();
        }   
        //holder.image.setLayoutParams(new GridView.LayoutParams(100, 100));

         //String item_name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));

            holder.image.setImageBitmap(photos.get(position).getBitmap());    

            //holder.item_name.setText(item_name);

        return convertView;
    }
}

static class ViewHolder {
   ImageView image;
   TextView item_name;
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    Cursor cursor = null;
    int image_column_index = 0;
    String[] proj = {MediaStore.Images.Media.DATA};

    String state = Environment.getExternalStorageState();

  if(Environment.MEDIA_MOUNTED.equals(state)){


    cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,proj,null, null,null);
  }else{
      cursor = managedQuery(MediaStore.Images.Media.INTERNAL_CONTENT_URI,proj,null, null,null);

  }
    cursor.moveToPosition(position);
    image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);


    String info = cursor.getString(image_column_index);
    Intent imageviewer  = new Intent(getApplicationContext(), ViewImage.class);
           imageviewer.putExtra("pic_name", info);
           startActivity(imageviewer);

     cursor.close();
}

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 2;

if (height > reqHeight || width > reqWidth) {
    if (width > height) {
        inSampleSize = Math.round((float)height / (float)reqHeight);
    } else {
        inSampleSize = Math.round((float)width / (float)reqWidth);
    }
}

/*final int REQUIRED_SIZE = 180;

int scale = 1;
if(reqWidth > REQUIRED_SIZE || reqHeight > REQUIRED_SIZE){
    scale = (int)Math.pow(2, (int)Math.round(Math.log(REQUIRED_SIZE/(double)Math.max(reqHeight, reqWidth)) / Math.log(0.5)));
}*/

return inSampleSize;

}

异步任务中的其他方法:

private void loadImages() {
 final Object data = getLastNonConfigurationInstance();
 if(data == null){
     new LoadImagesFromSDCard().execute();
 }else {
     final LoadedImage[] photos = (LoadedImage[])data;
     if(photos.length == 0){

        new LoadImagesFromSDCard().execute();
     }
     for(LoadedImage photo:photos){
         addImage(photo);
    }
 }

}

private void addImage(LoadedImage... value) {

    for(LoadedImage photo: value){
        imageAdapter.addPhotos(photo);  
        imageAdapter.notifyDataSetChanged();
    }

}

我假设在从ListView错误中获取logcat之前我应首先获取根元素,但我不知道如何,我似乎无法从中找到合适的方法文档。

P.S:我使用的是Activity,而不是ListActivity

1 个答案:

答案 0 :(得分:1)

该异常告诉您尝试执行无效的转换。我的猜测是你的行布局不像你在ImageView中使用强制转换所假设的那样简单onRetainNonConfigurationInstance(),而是你的行布局可能有一个包裹{{1}的父Linearlayout (和其他观点?!)。当您调用方法ImageView时,您会获得您尝试投放到getChildAt的父Linearlayout。相反,你应该这样做:

ImageView

注意://... for(int i = 0; i < count; i++){ LinearLayout parent = (LinearLayout)list.getChildAt(i); final ImageView v = (ImageView) parent.findViewById(R.id.the_id_of_the_imageview_from_theRow_layout); mylist[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap()); } 方法仅返回可见行的行视图,因此,如果您尝试访问当前不可见的行的getChildAt,则很可能最终会a View(您应该直接从适配器获取drawable,而不是解析所有行NullPointerException)。

根据评论进行修改

如果我理解你的问题,你认为这是正常的行为。您没有说明最终如何获得Views中的位图,但我猜您只是保存用户当前可见的onRetainNonConfigurationInstance行中的图像。当需要使用ListView中的图像恢复适配器时,您最终只能获得之前可见的图像。如果再次旋转手机,情况会变得更糟,因为很可能在横向方向上可见的图像较少,而在纵向方向上可以看到。

我不知道这是否有效,但您可以尝试修改getLastNonConfigurationInstance()类来存储LoadedImage图像的id。在保存配置时,停止MediaStore.Images.Media任务并取消所有图片(从LoadImagesFromSDCard中移除Bitmap LoadedImage } LoadedImages(在适配器中),除了用户当前可见的那些。通过适配器发送照片 ArrayList ArrayList

当需要恢复适配器时,将新适配器的照片 onRetainNonConfigurationInstance设置为您返回的那个,并将ArrayList放到可见图像的位置(以便用户看到发送的图像)。在适配器的ListView方法中,如果getView的值为null,则会放置默认图像(例如 loading ... )关联的Bitmap类。启动新的LoadedImage任务以获取图像并再次解析它们。当您从LoadImagesFromSDCard获得id时,您会检查适配器中是否存在MediaStore.Images.Media LoadedImage以及是否设置了id 。如果它不存在(或者它没有关联Bitmap),则加载图像并将其放入适配器中,否则图像已经存在并移动到下一个图像。

我不知道上述解决方案是否有效或是否有效。我建议你修改代码并按需加载图片,而不是在一项大任务中加载所有内容。