Asked  7 Months ago    Answers:  5   Viewed   33 times

From android developer (Creating Lists and Cards):

The RecyclerView widget is a more advanced and flexible version of ListView.

Okay, it sounds cool, but when I saw this example picture, I got really confused about the difference between these two.

enter image description here

The picture above can be easily created by ListView using custom adapter.

So, in what situation should one use RecyclerView?

 Answers

85

RecyclerView was created as a ListView improvement, so yes, you can create an attached list with ListView control, but using RecyclerView is easier as it:

  1. Reuses cells while scrolling up/down - this is possible with implementing View Holder in the ListView adapter, but it was an optional thing, while in the RecycleView it's the default way of writing adapter.

  2. Decouples list from its container - so you can put list items easily at run time in the different containers (linearLayout, gridLayout) with setting LayoutManager.

Example:

mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//or
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
  1. Animates common list actions - Animations are decoupled and delegated to ItemAnimator.

There is more about RecyclerView, but I think these points are the main ones.

So, to conclude, RecyclerView is a more flexible control for handling "list data" that follows patterns of delegation of concerns and leaves for itself only one task - recycling items.

Tuesday, June 1, 2021
 
sohum
answered 7 Months ago
94

I've figured it out. I couldn't understand how the hell the adapter started and how did it know where to get the data from. When i extended the BaseAdapter class, in the constructor of that class I initialized the list of items that I wanted to see in the ListView. But I couldn't figure out how these values would be used and when.

So here's the thing !!! :

In the BaseAdapter there are some methods that need to be overridden. Among these, there is getCount().

When the ListView is created and whatnot, it calls getCount(). If this returns a value different than 0 (I returned the size of the ArrayList which I've previously initialized in the constructor), then it calls getView() enough times to fill the screen with items. For instance, I initialized the ArrayList with 20 items. Because only 8 items initially fit on the screen, getView() was called 8 times, each time asking for the position it required for me to return (more precisely it wanted to know how the row would look like in the list on that specific position, what data it needed to contain). If I scroll down the list, getView() gets called over and over again, 'til I hit the end of the list, in my case 20 items / rows.

What notifyDataSetChanged() does is ... when called, it looks at what items are displayed on the screen at the moment of its call (more precisely which row indexes ) and calls getView() with those positions.

i.e. if you're displaying the first 8 items in the list (so those are the ones visible on the screen) and you add another item between the 2nd and 3rd item in the list and you call notifyDataSetChanged() then getView() is called 8 times, with positions starting from 0 and ending with 7, and because in the getView() method you're getting data from the ArrayList then it will automatically return the new item inserted in the list alongside 7 out of the previous 8 (7 and not 8 because the last item went one position down, so it is not visible anymore), and the ListView will redraw, or whatever, with these items.

Also, important to specify is that if you've implemented getView() correctly, you'll end up recycling the items (the objects) already displayed (instead of creating new ones). See this video at around 12:00 minutes to see the correct way to implement getView()

I've figured all this out by placing calls to LogCat in every method and following what was going on.

Hope this helps someone who's just now starting to understand how ListViews work.

P.S. This example also helped me a lot to understand.

UPDATE

Nowadays ListViews are not really used anymore. Android came out with the RecyclerView which does the recycling of the views for you, but knowing the basics of a ListView helps with understanding the RecyclerView.

Here's a link for reference: https://developer.android.com/guide/topics/ui/layout/recyclerview

Saturday, June 5, 2021
 
Marcelo
answered 6 Months ago
67

You have to extend FragmentPagerAdapter or FragmentStatePagerAdapter in order to easily embed RecyclerView. If you are going to update your ViewPager contents during its lifecycle it is strictly recommended to use FragmentStatePagerAdapter

You will have to create additional fragment layout, containing RecyclerView.

If you wish to update your ViewPager with SwipeRefreshLayout, don't wrap it with SwipeRefreshLayout. Instead, you must have SwipeRefreshLayout inside fragment layout.

Therefore for your fragment you may get the following xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/listRefresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/categoryList"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>

And create additional Fragment class, which will inflate that layout and implement methods, that you will need to update refresh indicator status.

A bit old example is found here: http://developer.android.com/training/implementing-navigation/lateral.html

If you wish to connect your ViewPager with new support library TabLayout, it is easily one with:

tabLayout.setupWithViewPager(viewPager);

Finally, if you will update your "fragmented" ViewPager, don't try to reset the adapter, as fragments are managed not with adapter, but with FragmentManager. It is wiser to update content of corresponding RecyclerViews

public class MyFragmentedPagerAdapter extends FragmentStatePagerAdapter {
    private final TabLayout mTabLayout;
    private final SwipeRefreshLayout.OnRefreshListener mRefreshListener;
    private Vector<PriceListFragment> fragmentList;
    private Vector<String> titles;

    public MyFragmentedPagerAdapter(FragmentManager fm, MyComplexData data, OnCartActionListener listener, TabLayout tabLayout, SwipeRefreshLayout.OnRefreshListener refreshListener) {
        super(fm);
        mTabLayout = tabLayout;

        // external refresh listener, that will trigger an updateData() 
        mRefreshListener = refreshListener;

        fragmentList = new Vector<>();
        titles = new Vector<>();
        updateData(data);
    }

    public void updateData(MyComplexData data) {
        boolean updateTabs = false;
        boolean hasNewData = false;

        Vector<String> newTitles = new Vector<>();

        int position = 0;
        for(TabContents tabContents : data.getTabContents()) {

            if(tabContents.getListContents() == null)
                continue;
            hasNewData = true;
            boolean isNewFragment;

            MyFragment fragment;
            try {
                fragment = fragmentList.get(position);
                isNewFragment = false;
            } catch (ArrayIndexOutOfBoundsException e) {
                fragment = new MyFragment();
                isNewFragment = true;
            }
            // Update the data, title and hide update indicator of SwipeRefreshLayout
            fragment.setTabContents(tabContents);

            newTitles.add(tabContents.getName());

            if(isNewFragment) {
                fragment.setRefreshListener(mRefreshListener);
                fragmentList.add(fragment);
            }
            position++;
        }

        if(!hasNewData)
            return;

        // we need to decide, whether to update tabs
        if(titles.size() != newTitles.size()) {
            updateTabs = true;
        } else {
            for(position = 0; position < titles.size(); position++) {
                if(!titles.get(position).equals(newTitles.get(position))) {
                    updateTabs = true;
                    break;
                }
            }
        }

        titles = newTitles;
        notifyDataSetChanged();

        if(updateTabs)
            mTabLayout.setTabsFromPagerAdapter(this);
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    // You need to override this method as well
    @Override
    public int getItemPosition(Object object) {
        MyFragment fragment = (MyFragment) object;
        String title = (String) fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return fragmentList.get(position).getTitle();
    }
}

Your MyFragment class has to implement getTitle() method.

Monday, August 2, 2021
 
ajaybc
answered 4 Months ago
15

you can use requiresFadingEdge attribute in your xml. make a listview inside your layout with this attributes:

    android:fadingEdge="horizontal"
    android:fadingEdgeLength="30dp"
    android:fillViewport="false"
    android:requiresFadingEdge="vertical"

so as your items loaded inside the listview it will have faded bounds on its top and bottom like this image below:

fading edge list

so your code should be something like this:

<RelativeLayout 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:background="@drawable/th"
  tools:context="com.example.maghari_se.testfading.MainActivity">
<ListView
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:layout_alignParentBottom="true"
    android:visibility="visible"
    android:fadingEdge="horizontal"
    android:fadingEdgeLength="30dp"
    android:fillViewport="false"
    android:requiresFadingEdge="vertical">

</ListView>

Tuesday, September 7, 2021
 
z5h
answered 3 Months ago
z5h
57

This happens because the views get recycled and reused.

So when the view gets recycled, it retains properties of the "old" view if you don't change them again. So when you scroll down to number 12, the view that used to hold number 1 gets recycled (as it can't be seen on the screen anymore), and is used to create number 12. This is why the blue color is on number 12.

When the item is, for example, clicked, you'll need to save a "clicked" value into your POJO object. Then when the item is drawn, check that value and set the correct image / background color depending on that value.

I've done this in the below code, so it should give you a rough idea of what to do:

@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    TextView title = (TextView) holder.view.findViewById(R.id.title);
    final TextView desc = (TextView) holder.view.findViewById(R.id.desc);
    final ImageView imageView = (ImageView) holder.view.findViewById(R.id.imageView);

    final MyPojo pojo = pojos.get(position);

    title.setText(pojo.getTitle());
    if(!pojo.clicked) {
        desc.setText(pojo.getDesc());
        imageView.setImageResource(pojo.getImage());
        desc.setBackgroundColor(Color.argb(0,0,0,0));
    } else {
        desc.setText("clicked");
        desc.setBackgroundColor(Color.BLUE);
        imageView.setImageResource(R.drawable.heart_red);
    }

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            pojo.clicked = true;
            desc.setText("clicked");
            desc.setBackgroundColor(Color.BLUE);
            imageView.setImageResource(R.drawable.heart_red);
        }
    });
}

And i've added a "clicked" boolean to the MyPojo class.

public class MyPojo {

    String title;
    String desc;
    int image;
    boolean clicked;
 }
Saturday, September 11, 2021
 
JoJo
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share