Update log:

  • 2016.08.28 update, based on version 2.2.0, mainly updated header and footer support to hide or show
  • Updated on June 21, 2016, based on version 2.1.0, with major updates to header and footer support, as well as new versions to load more apis

One of the most difficult things to do when writing lists is to write one Adapter for each different list. In this way, the average project will have dozens of Adapters. If the Adapter has other functions such as multiple items and loading more items, it will be even more troublesome. It’s even more painful to have to write the same thing multiple times on different pages

In fact, a careful analysis shows that each Adapter consists of two parts

  • The first is the Adapter itself, which handles getView, itemType, loading more, and maintaining data
  • The second is Item, which creates convertView, sets data, sets, and handles events

Can we write a generic Adapter that handles all itemViews? I’ve learned from years of experience that this is impossible. The only obstacle is the ItemView, and each ItemView is very different.

So what should our ideal Adapter look like?

  • The Item section is completely independent, allowing one definition to be used everywhere
  • Support header and footer, GridView, RecyclerView also support header and footer
  • Come with more functions

All of the so-called universal Adapters you can search for (here is a more complete example) end up letting you pass in a layout ID and then setting the data through an interface. In fact, this implementation is fine for some simple scenarios, but when it comes to itemType, the complexity increases exponentially. Now, almost all of the more complex lists are dependent on itemType, and there is no way to use one definition everywhere

AssemblyAdapter

The GitHub Portal is a new Adapter library called AssemblyAdapter. The GitHub Portal is a new Adapter library called AssemblyAdapter.

  • Item is used everywhereYou simply write an ItemFactory for each Item Layout and use the ItemFactory
  • Convenient combination of multiple items. You can use multiple ItemFactories, each of which represents an itemType
  • Support header and footerUse AssemblyAdapter to make ExpandableListView, GridView, RecyclerView, ViewPager support header and footer
  • Feel free to hide and display the header or footer.header and footer also support hiding or display control through their setEnabled(Boolean) methods
  • Come with more functionsSlide to the bottom of the list to trigger more loading. You just need to define an ItemFactory specifically for loading more
  • Support common Adapters. Support BaseAdapter, RecyclerView. Adapter, BaseExpandableListAdapter, PagerAdapter, FragmentPagerAdapter and FragmentStatePagerAdapter, covers most of the Adapter is commonly used in Android development
  • No performance loss. No reflection related technology is used, so there is no need to worry about performance

There are six types of Adapter:

Adapter The parent class Apply to Support functions
AssemblyAdapter BaseAdapter ListView, GridView, Spinner, Gallery Multiple items, headers and footers, load more
AssemblyRecyclerViewAdapter RecyclerView.Adapter RecyclerView Multiple items, headers and footers, load more
AssemblyExpandableAdapter BaseExpandableListAdapter ExpandableListView Multiple items, headers and footers, load more
AssemblyPagerAdapter PagerAdapter ViewPager + View Multiple items, headers, and footers
AssemblyFragmentPagerAdapter FragmentPagerFragment ViewPager + Fragment Multiple items, headers, and footers
AssemblyFragmentStatePagerAdapter FragmentStatePagerFragment ViewPager + Fragment Multiple items, headers, and footers

An AssemblyAdapter is an example of an AssemblyAdapter. You can use an AssemblyAdapter as an example. You can use an ItemFactory and an Item to inherit their own classes

The AssemblyAdapter is divided into three parts:

  • Adapter: Is responsible for maintaining data, ItemTypes, and loading more states
  • ItemFactory: Is responsible for matching data and creating items
  • Item: Is responsible for everything about the itemView, including creating the itemView, setting data, setting, and handling events

The AssemblyAdapter differs fundamentally from other universal adapters in that it defines all item-related processing in an ItemFactory class. An AssemblyItemFactory method is used to add an ItemFactory to the Adapter.

The advantage of this is that you can actually use one definition everywhere, and you can easily use multiple ItemFactories on a page by calling the addItemFactory(AssemblyItemFactory) method multiple times. This is exactly what AssemblyAdapter’s name means

In addition, since an Adapter supports multiple items and has only one data list, the data type of the data list must be Object

3. Create ItemFactory

To use AssemblyAdapter, you need to create an ItemFactory and Item like this:

public class UserItemFactory extends AssemblyItemFactory<UserItemFactory.UserItem> {

    @Override
    public boolean isTarget(Object itemObject) {
        return itemObject instanceof User;
    }

    @Override
    public UserListItem createAssemblyItem(ViewGroup parent) {
        return new UserListItem(R.layout.list_item_user, parent);
    }

    public class UserItem extends AssemblyItem<User> {
        private ImageView headImageView;
        private TextView nameTextView;
        private TextView sexTextView;
        private TextView ageTextView;
        private TextView jobTextView;

        public UserListItem(int itemLayoutId, ViewGroup parent) {
            super(itemLayoutId, parent);
        }

        @Override
        protected void onFindViews(View itemView) {
            headImageView = (ImageView) findViewById(R.id.image_userListItem_head);
            nameTextView = (TextView) findViewById(R.id.text_userListItem_name);
            sexTextView = (TextView) findViewById(R.id.text_userListItem_sex);
            ageTextView = (TextView) findViewById(R.id.text_userListItem_age);
            jobTextView = (TextView) findViewById(R.id.text_userListItem_job);
        }

        @Override
        protected void onConfigViews(Context context) {
            getItemView().setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    Toast.makeText(v.getConext(), "The first" + (getPosition() + 1) + "Piece of data", Toast.LENGTH_LONG).show(); }}); }@Override
        protected void onSetData(int position, User user) { headImageView.setImageResource(user.headResId); nameTextView.setText(user.name); sexTextView.setText(user.sex); ageTextView.setText(user.age); jobTextView.setText(user.job); }}}Copy the code

A:

  • ItemFactory genericsIs to qualify the type returned by its createAssemblyItem(ViewGroup) method
  • The ItemFactoryisTarget()The isTarget(Object) method is used to match the data in the list. The Adapter calls the isTarget(Object) method of its ItemFactory when it retrieves the current location from the list. Whoever returns true will process the current item
  • The ItemFactorycreateAssemblyItem(ViewGroup)Method to create an Item that returns the same type as the generic type you configured on ItemFactory
  • Generic ItemIs used to specify the corresponding data type and is used in the onSetData and getData() methods
  • The Item ofonFindViews(View)andonConfigViews(Context)Methods are used to initialize and configure the View, respectively, only when an Item is createdOne callAnd in the onFindViews method you can use it directlyfindViewById(int)Method to get the View
  • The Item ofonSetData()Method is used to set the data inThis is called every time getView() is called
  • You can pass ItemgetPosition()andgetData()Method to retrieve the current location and data, so you no longer need to use setTag() to bind the location and data to click
  • You can also pass ItemgetItemView()Method to get the current itemView

You can also use contentSetters to simplify your code, as shown in the following example:

public class UserItemFactory extends AssemblyItemFactory<UserListItemFactory.UserListItem> {

    @Override
    public boolean isTarget(Object itemObject) {
        return itemObject instanceof User;
    }

    @Override
    public UserListItem createAssemblyItem(ViewGroup parent) {
        return new UserListItem(R.layout.list_item_user, parent);
    }

    public class UserItem extends AssemblyItem<User> {
        public UserListItem(int itemLayoutId, ViewGroup parent) {
            super(itemLayoutId, parent);
        }

        @Override
        protected void onFindViews(View itemView) {}@Override
        protected void onConfigViews(Context context) {
            getItemView().setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    Toast.makeText(v.getConext(), "The first" + (getPosition() + 1) + "Piece of data", Toast.LENGTH_LONG).show(); }}); }@Override
        protected void onSetData(int position, User user) { getSetter() .setImageResource(R.id.image_userListItem_head, user.headResId) .setText(R.id.text_userListItem_name, user.name) .setText(R.id.text_userListItem_sex, user.sex) .setText(R.id.text_userListItem_age, user.age) .setText(R.id.text_userListItem_job, user.job); }}}Copy the code

4. Use ItemFactory

AssemblyItemFactory (AssemblyItemFactory) Add an AssemblyItemFactory (AssemblyItemFactory) to the Adapter.

ListView listView = ... ; List<Object> dataList =new ArrayList<Object>;
dataList.add(new User("Quarantine old Wang."));
dataList.add(new User("Old Li next door"));

AssemblyAdapter adapter = new AssemblyAdapter(dataList);
adapter.addItemFactory(new UserItemFactory());

listView.setAdapter(adapter);
Copy the code

You can also use multiple ItemFactories at once, as follows:

ListView listView = ... ; List<Object> dataList =new ArrayList<Object>;
dataList.add(new User("Quarantine old Wang."));
dataList.add(new Game(League of Legends));
dataList.add(new User("Old Li next door"));
dataList.add(new Game(Overwatch));

AssemblyAdapter adapter = new AssemblyAdapter(dataList);
adapter.addItemFactory(new UserItemFactory());
adapter.addItemFactory(new GameItemFactory());

listView.setAdapter(adapter);
Copy the code

5. Use header and footer

AssemblyAdapter supports adding headers and footers, making it easy to anchor content at the top or bottom of a list. Adapter support for headers and footers is important because you can make GridView, RecyclerView, and so on support headers and footers

Start by defining an ItemFactory for a header or footer

Add header and footer:

Add addHeaderItem(AssemblyItemFactory, Object) or addFooterItem(AssemblyItemFactory, Object).

AssemblyAdapter adapter = new AssemblyAdapter(objects);

adapter.addHeaderItem(new HeaderItemFactory(), "I have a small forehead!"); . adapter.addFooterItem(new HeaderItemFactory(), "I am little tail!");
Copy the code

AddHeaderItem (AssemblyItemFactory, Object) and addFooterItem(AssemblyItemFactory, Object). The second parameter of addHeaderItem and addFooterItem is the data required by the Item

Hide or display the header and footer

Either addHeaderItem() or addFooterItem() returns a FixedItemInfo object that controls header or footer, as follows:

AssemblyAdapter adapter = new AssemblyAdapter(objects);

FixedItemInfo userFixedItemInfo = adapter.addHeaderItem(new HeaderItemFactory(), "I have a small forehead!");

/ / hide
userFixedItemInfo.setEnabled(false);

/ / show
userFixedItemInfo.setEnabled(true);
Copy the code

Given the header and footer, the position obtained by the item.getPosition () method is the position of the Item in the Adapter. The actual position of the Item in the part of the Adapter can be obtained by using getPositionInPart(int)

6. Use load more features

First you need to create an inherited from AssemblyLoadMoreItemFactory ItemFactory, AssemblyLoadMoreItemFactory already will load more related logical part of writing good code, you just need to care about interface, as follows:

public class LoadMoreItemFactory extends AssemblyLoadMoreItemFactory {

    public LoadMoreListItemFactory(OnLoadMoreListener eventListener) {
        super(eventListener);
    }

    @Override
    public AssemblyLoadMoreItem createAssemblyItem(ViewGroup parent) {
        return new LoadMoreListItem(R.layout.list_item_load_more, parent);
    }

    public class LoadMoreItem extends AssemblyLoadMoreItem {
        private View loadingView;
        private View errorView;
        private View endView;

        public LoadMoreListItem(int itemLayoutId, ViewGroup parent) {
            super(itemLayoutId, parent);
        }

        @Override
        protected void onFindViews(View itemView) {
            loadingView = findViewById(R.id.text_loadMoreListItem_loading);
            errorView = findViewById(R.id.text_loadMoreListItem_error);
            endView = findViewById(R.id.text_loadMoreListItem_end);
        }

        @Override
        public View getErrorRetryView(a) {
            return errorView;
        }

        @Override
        public void showLoading(a) {
            loadingView.setVisibility(View.VISIBLE);
            errorView.setVisibility(View.INVISIBLE);
            endView.setVisibility(View.INVISIBLE);
        }

        @Override
        public void showErrorRetry(a) {
            loadingView.setVisibility(View.INVISIBLE);
            errorView.setVisibility(View.VISIBLE);
            endView.setVisibility(View.INVISIBLE);
        }

        @Override
        public void showEnd(a) { loadingView.setVisibility(View.INVISIBLE); errorView.setVisibility(View.INVISIBLE); endView.setVisibility(View.VISIBLE); }}}Copy the code

And then calling Adapter setLoadMoreItem (AssemblyLoadMoreItemFactory) method set to load more ItemFactory can, as follows:

AssemblyAdapter adapter = ... ; adapter.setLoadMoreItem(new LoadMoreItemFactory(new OnLoadMoreListener(){
    @Override
    public void onLoadMore(AssemblyAdapter adapter) {
        // Access the network to load data.booleanloadSuccess = ... ;if (loadSuccess) {
            // Check whether the load is complete when it succeeds, then call the Adapter setLoadMoreEnd(Boolean) method to set whether the load is complete
            booleanloadMoreEnd = ... ; adapter.setLoadMoreEnd(loadMoreEnd); }else {
            // When the loading fails, the loadMoreFailed() method of Adapter is called to display a loading failure message. If the user clicks the failure message, more loading will be triggered againadapter.loadMoreFailed(); }}}));Copy the code

You can also control whether to disable loading more functions by using the setDisableLoadMore(Boolean) method instead of setLoadMoreEnd(Boolean), The difference is that setLoadMoreEnd(Boolean) displays the end prompt at the end of the list when it is true, while setDisableLoadMore(Boolean) does not show loading more tails at all