AbhayBohra AbhayBohra - 2 months ago 13
Android Question

App crashes when adding new data to the viewpager

I have created a ViewPager in which i am using bitmapArray to display images in viewpager.Every thing works fine but when i add a new data and call notifydatasetchange,the app crashes.

In this App i am downloading songs and from that file i am fetching the bitmap from that downloaded song and add in the bitmap array.

It gives the following Execption:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.


I am unable to find what causes this execption

Adapter class for ViewPager is :

public class CoverFlowPagerAdapter extends PagerAdapter {

ArrayList<Bitmap> mBitmapArray;
Context c;

interface CoverFlowClick {
public void coverClick(int coverFlowPosition);
}

CoverFlowClick mCoverFlowClick;

public CoverFlowPagerAdapter(Context c, ArrayList<Bitmap> mBitmapArray) {
this.c = c;
this.mBitmapArray = mBitmapArray;
mCoverFlowClick = (CoverFlowClick) c;
}

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

@Override
public boolean isViewFromObject(View view, Object object) {
return (object == view);
}

@Override
public Object instantiateItem(ViewGroup container, final int position) {

ImageView imageView;
View v = null;
LayoutInflater inflater= (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v=inflater.inflate(R.layout.coverflow_item,null);
imageView= (ImageView) v.findViewById(R.id.coverFlowImage);

imageView.setImageBitmap(mBitmapArray.get(position));

container.addView(imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(c, "Clicked::--" + position, Toast.LENGTH_SHORT).show();
mCoverFlowClick.coverClick(position);
}
});

return imageView;
}

@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((ImageView) object);
}
}


I am adding the bitmap to array and calling notifydatasetchanged like this:

@Override
public void updateSongList(String songName) {

getBitmapArray(songName);
mCoverFlowAdapter.notifyDataSetChanged();

}


EDIT: My custom Layout File is



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<ImageView
android:id="@+id/coverFlowImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>


and my getBitmapArray function is

public void getBitmapArray(String songName) {

Bitmap bitmap = null;
path = Environment.getExternalStorageDirectory() + "/AppDownloads/" + songName;
File file = new File(path);
FileInputStream inputStream = null;

try {
inputStream = new FileInputStream(file.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
}

try {
metaRetriver.setDataSource(inputStream.getFD());
} catch (IOException e) {
e.printStackTrace();
}

art = metaRetriver.getEmbeddedPicture();
if (art != null) {
bitmap = BitmapFactory
.decodeByteArray(art, 0, art.length);
}

if (bitmap != null) {
mBitmapArray.add(bitmap);
} else {
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.unknown_album);
mBitmapArray.add(icon);
}
}

Answer

Instead of making LinearLayout as parent of ImageView, let ImageView be the ROOT of the Layout as follows:-

<ImageView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/coverFlowImage"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

In the adapter:-

@Override
public Object instantiateItem(ViewGroup container, final int position) {

    LayoutInflater inflater= (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ImageView imageView = (ImageView) inflater.inflate(R.layout.coverflow_item, container, false);
    imageView.setImageBitmap(mBitmapArray.get(position));

    container.addView(imageView);
    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(c, "Clicked::--" + position, Toast.LENGTH_SHORT).show();
            mCoverFlowClick.coverClick(position);
        }
    });

    return imageView;
}

If you want to use a LinearLayout as ROOT then instead of adding ImageView to the container, add the inflated view and also return it in the instantiateItem method.

Also change destroyItem method to

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    container.removeView(object);
}
Comments