Search code examples
androidandroid-fragmentsandroid-pageradapter

ViewPagers and PagerAdapters


I am new to Android and am trying a sample application for showing ViewPagers in a Master-Detail Flow using custom PagerAdapters and FragmentStatePagerAdapters. My application has a list of dummy items managed by a SQLiteDatabase which contain a title String, a description String, a Boolean like status, and a list of images (I plan to implement them as downloading from String urls but presently I'm just trying with a single image resource). I am having two problems in the Detail View.

My intention is to use a ViewPager with a FragmentStatePagerAdapter to show the detail view, which consists of a ViewPager with a custom PagerAdapter for showing the list of images, TextView for title and description, a ToggleButton for the like status and a delete button for deleting items from the list.

Issues:

  1. The ViewPager with the custom PagerAdapter does not display the image. It occupies the expected space and swipes performed on it also behave as expected. Only the image is not visible.
  2. [RESOLVED] On using the delete button, I am able to delete the item from the database, and also update the Master View accordingly, but I am not able to update the Detail View, and the app crashes.

Here is my code:

Code that calls ItemDetailActivity.java

@Override
public void onClick(View v) {
    Intent detailIntent = new Intent(getContext(), ItemDetailActivity.class);
    detailIntent.putExtra(ItemDetailFragment.ARG_LIST_POSITION, holder.position);
    getContext().startActivity(detailIntent);
}

ItemDetailActivity.java

public class ItemDetailActivity extends FragmentActivity {

    static ItemDetailPagerAdapter idpa;

    static ViewPager detailPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_item_detail);

        idpa = new ItemDetailPagerAdapter(getSupportFragmentManager());

        // Show the Up button in the action bar.
        getActionBar().setDisplayHomeAsUpEnabled(true);

        detailPager = (ViewPager) findViewById(R.id.item_detail_container);
        detailPager.setAdapter(idpa);
        detailPager.setCurrentItem(getIntent().getIntExtra(ItemDetailFragment.ARG_LIST_POSITION, 0));
    }
}

activity_item_detail.xml

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/item_detail_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.trial.piclist.ItemDetailActivity"
    tools:ignore="MergeRootFrame" />

ItemDetailFragment.java

public class ItemDetailFragment extends Fragment {
    public static final String ARG_ITEM_ID = "item_id";
    public static final String ARG_LIST_POSITION = "list_index";
    public static final String ARG_TWO_PANE = "is_two_pane";
    int position = -1;
    long id = -1;
    boolean twoPane = false;
    ViewPager pager;
    private PicItem mItem;

    public ItemDetailFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        twoPane = getArguments().getBoolean(ARG_TWO_PANE, false);
        position = getArguments().getInt(ARG_LIST_POSITION, -1);
        id = getArguments().getLong(ARG_ITEM_ID, -1);
        if (id == -1)
            id = ItemListFragment.getIdByPosition(position);
        setmItem(id);
    }

    public void setmItem(long id) {
        if (id >= 0) {
            try {
                ItemListActivity.lds.open();
                mItem = ItemListActivity.lds.getById(id);
                ItemListActivity.lds.close();
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            if (mItem != null) {
                List<String> pics = new ArrayList<String>();
                pics.add("1");
                pics.add("2");
                pics.add("3");
                pics.add("4");
                pics.add("5");
                mItem.setPics(pics);
            }
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_item_detail,
                container, false);
        DetailViewHolder holder = new DetailViewHolder();

        pager = (ViewPager) rootView.findViewById(R.id.pager);
        ImagePagerAdapter adapter = new ImagePagerAdapter(mItem, getActivity(),
                inflater, position);
        pager.setAdapter(adapter);

        holder.position = getArguments().getInt(ARG_LIST_POSITION);
        holder.ttv = (TextView) rootView.findViewById(R.id.item_title);
        holder.dtv = (TextView) rootView.findViewById(R.id.item_detail);
        holder.likeButton = (ToggleButton) rootView
                .findViewById(R.id.item_like);
        holder.deleteButton = (Button) rootView.findViewById(R.id.item_delete);
        rootView.setTag(holder);

        if (mItem != null) {

            holder.ttv.setText(mItem.getTitle());
            holder.dtv.setText(mItem.getDescription());
            holder.likeButton.setChecked(mItem.getIsLiked());
            holder.likeButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    ItemListActivity.lds.open();
                    ItemListActivity.lds.toggleLike(mItem.getId());
                    mItem.toggleIsLiked();
                    ItemListActivity.lds.close();
                    ItemListFragment.listDisplayHelper.toggleLiked(position);
                }
            });
            holder.deleteButton.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    ItemListActivity.lds.open();
                    ItemListActivity.lds.removeItem(mItem.getId());
                    ItemListActivity.lds.close();

                    ItemListFragment.listDisplayHelper.remove(position);
                    ItemListActivity.idpa.notifyDataSetChanged();
                    // What do I do so that the FragmentStatePagerAdapter is
                    // updated and the viewpager shows the next item.
                }
            });
        }

        return rootView;
    }

    static private class DetailViewHolder {
        TextView ttv;
        TextView dtv;
        ToggleButton likeButton;
        Button deleteButton;
        int position;
    }
}

fragment_item_detail.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context="com.trial.piclist.ItemDetailFragment" >
    <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="200dip">
    </android.support.v4.view.ViewPager>

    <TableRow
        android:id="@+id/tableRow1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/item_title"
            style="?android:attr/textAppearanceLarge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello"
            android:textIsSelectable="true" />

        <Space
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <include
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            layout="@layout/controls_layout" />
    </TableRow>

    <ScrollView
        android:id="@+id/descScrollView"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/item_detail"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/hello" />
        </LinearLayout>
    </ScrollView>

</LinearLayout>

controls_layout.xml

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

<ToggleButton
    android:id="@+id/item_like"
    android:layout_width="30dip"
    android:layout_height="30dip"
    android:layout_gravity="right"
    android:background="@android:drawable/btn_star"
    android:gravity="center"
    android:text="@string/like_list_item"
    android:textOff="@string/empty_text"
    android:textOn="@string/empty_text" />

<Button
    android:id="@+id/item_delete"
    style="?android:attr/buttonStyleSmall"
    android:layout_width="30dip"
    android:layout_height="30dip"
    android:background="@android:drawable/ic_menu_delete"
    android:text="@string/empty_text" />

</LinearLayout>

Custom PagerAdapter ImagePagerAdapter.java

public class ImagePagerAdapter extends PagerAdapter {
    LayoutInflater inflater;
    List<View> layouts = new ArrayList<>(5);

    // Constructors.

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (layouts.get(position) != null) {
            return layouts.get(position);
        }
        View layout = inflater.inflate(R.layout.detail_image, 
            ((ViewPager) container), true);
        try {
            ImageView loadSpace = (ImageView) layout
                    .findViewById(R.id.detail_image_view);
            loadSpace.setBackgroundColor(0x000000);
            loadSpace.setImageResource(R.drawable.light_grey_background);
            loadSpace.setAdjustViewBounds(true);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        layout.setTag(images.get(position));
        layouts.set(position, layout);
        return layout;
    }

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

    @Override
    public int getCount() {
        return 5;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return (((View) object).findViewById((view.getId())) != null);
    }

}

FragmentPagerAdapter ItemDetailPagerAdapter.java

public class ItemDetailPagerAdapter extends FragmentStatePagerAdapter {

    public ItemDetailPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = new ItemDetailFragment();
        Bundle args = new Bundle();
        args.putLong(ItemDetailFragment.ARG_ITEM_ID, ItemListFragment.getIdByPosition(position));
        args.putInt(ItemDetailFragment.ARG_LIST_POSITION, position);
        args.putBoolean(ItemDetailFragment.ARG_TWO_PANE, ItemListActivity.mTwoPane);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public int getCount() {
        openDatabase();
        int c = database.getCount();
        closeDatabase();
        return c;
    }

    @Override
    public int getItemPosition(Object object) {
        long mId = ((ItemDetailFragment) object).getmId();
        int pos = POSITION_NONE;
        openDatabase();
        if (database.contains(mId)) {
            pos = database.getPositionById(mId);
        }
        closeDatabase();
        return pos;
    }
}

Any help is much appreciated. Thanks :)


Solution

    1. In ImagePagerAdapter.java, correct the isViewFromObject method -

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

      This will correct the issue of the ImageView.

    2. In ItemDetailPagerAdapter.java, override the getItemPosition method -

      @Override
      public int getItemPosition(Object object) {
          int ret = POSITION_NONE;
          long id = ((ItemDetailFragment) object).getId();
          openDatabase();
          if (databaseContains(id)) {
              ret = positionInDatabase(id);
          }
          closeDatabase();
          return ret;
      }
      

      On deleting call the FragmentStatePagerAdapter.NotifyDataSetChanged() method. This will make the Adapter update itself on deleting. Although, the FragmentStatePagerAdapter uses a list of Fragments and of stored states to implement the adapter. That is also causing trouble. To remove that, implement your own list of Fragments.