2012-05-18 34 views
0

我希望標題不會誤導。我試圖執行加載圖像的AsyncTaskonRetainNonConfigurationInstance(),我得到這個錯誤"Java.lang.RuntimeException:Unable to retain Activity"。這裏是我的onRetainNonConfigurationInstance()代碼:從異步任務onRetainNonConfigurationInstance()中獲取ListView中的LinearLayout

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

} 

我假設我應該先把根元素之前,我從logcat錯誤ListView,但我不知道如何,我似乎無法從文檔中找到合適的方法。

P.S:我正在使用Activity而不是ListActivity

回答

1

該異常告訴您,您嘗試執行無效投射。我的猜測是,你的行佈局不是簡單的ImageView,就像你在onRetainNonConfigurationInstance()中的演員陣容一樣,而是你的行佈局可能有一個包含ImageView(和其他視圖?!)的父代Linearlayout。當您調用方法getChildAt時,您會收到您嘗試投射到ImageView的父母Linearlayout。相反,你應該這樣做:

//... 
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()); 
} 

注:只所以如果您嘗試訪問View某行當前不可見的,你很可能會結束的getChildAt方法返回可見行的行意見與NullPointerException(你應該從適配器直接得到drawable而不是解析所有行Views)。

編輯基於評論

如果我明白你的問題,你看它的行爲是正常的。您沒有說明您是如何獲得onRetainNonConfigurationInstance中的位圖的,但我的猜測是您只保存ListView行中當前對用戶可見的圖像。當需要使用getLastNonConfigurationInstance()中的圖像恢復適配器時,您最終只能看到以前可見的圖像。如果再次旋轉手機,情況會變得更糟,因爲很可能橫向顯示的圖像更少,而縱向顯示的圖像更少。

我不知道這是否會起作用,但您可以嘗試修改LoadedImage類來存儲MediaStore.Images.Media圖像的id。當它的時間來保存配置,停止LoadImagesFromSDCard任務和抵消所有的不同之處在於目前對用戶可見的那些在從LoadedImagesArrayList(在適配器)的圖像(刪除BitmapLoadedImage點)。通過 onRetainNonConfigurationInstance發送從您的適配器照片ArrayList

當它的時間來恢復適配器設置照片新的適配器你回來了一個ArrayList,把ListView到可見光圖像上的位置(所以用戶看到的發送圖像)。在您的適配器的getView方法中,當您在關聯的LoadedImage類中有Bitmapnull值時,您將放置默認圖像(如正在加載...)。開始新的LoadImagesFromSDCard任務以獲取圖像並再次解析它們。當您從MediaStore.Images.Media得到id你會檢查是否有此id一個LoadedImage存在於適配器,如果它有一個Bitmap設置它。如果它不存在(或沒有關聯Bitmap),則加載圖像並將其放入適配器,如果不存在,則圖像已存在並移至下一個圖像。

我不知道,如果上述解決方案將工作,或者如果它是有效的。我建議你修改代碼並按需加載圖片,而不是將所有內容加載到一個大任務中。

+0

感謝,它的工作原理,但唯一的麻煩,現在是,最後的圖像顯示在屏幕上時,屏幕方向變化僅在新的方向的佈局被顯示;但是您可以看到圖像仍在後臺加載。特別是當一個人點擊圖像時,它會在列表中顯示正確的圖像,但屏幕仍然顯示在方向中可見的最後一個圖像。希望我在這方面有所瞭解。任何想法如何解決它? – irobotxxx

+0

@sparrow我不明白,編輯你的問題,嘗試更好地解釋(並且發佈'AsyncTask'代碼和你設置圖像的自定義適配器(如果有的話))。當有方向改變時,活動將被重新創建,但'AsyncTask'將繼續執行它的後臺任務(所以在後臺看到它是正常的)。您可能只會看到最後一個可見圖像,因爲這些是您發送到新活動的唯一圖像。 – Luksprog

+0

我的意思是這樣的..如果我通過我在垂直方向滾動列表,當我切換到水平方向時,最後的可見光圖像(可以說4張圖片),現在在水平方向顯示和滾動足夠大剛爲那些可見的4張圖片。現在,如果我滾動以便我只能在水平方向上在屏幕上看到2個圖像,當我切換回垂直方向時,只有這2個圖像現在在屏幕上。它像它一直在切割它們。我發佈了我的異步任務和適配器代碼。 – irobotxxx