Showing posts with label RecyclerView. Show all posts
Showing posts with label RecyclerView. Show all posts

Tuesday, 22 November 2016

Recyclerview inside scrollview not scrolling smoothly

18:52:00 Posted by Kumanan , , ,
RecyclerView v = (RecyclerView) findViewById(...);
v.setNestedScrollingEnabled(false);

As an alternative, you can modify your layout using the support design library. I guess your current layout is something like:

<ScrollView >
    <LinearLayout >
       <View > <!-- upper content -->
            <RecyclerView > <!-- with custom layoutmanager -->
    </LinearLayout >
</ScrollView >

Wednesday, 20 January 2016

RecyclerView inside scrollview:- Does not scrolling issue

10:36:00 Posted by Kumanan , ,
public class MyLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

    private static boolean canMakeInsetsDirty = true;    private static Field insetsDirtyField = null;
    private static final int CHILD_WIDTH = 0;    private static final int CHILD_HEIGHT = 1;    private static final int DEFAULT_CHILD_SIZE = 100;
    private final int[] childDimensions = new int[2];    private final RecyclerView view;
    private int childSize = DEFAULT_CHILD_SIZE;    private boolean hasChildSize;    private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;    private final Rect tmpRect = new Rect();
    @SuppressWarnings("UnusedDeclaration")
    public MyLinearLayoutManager(Context context) {
        super(context);        this.view = null;    }

    @SuppressWarnings("UnusedDeclaration")
    public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);        this.view = null;    }

    @SuppressWarnings("UnusedDeclaration")
    public MyLinearLayoutManager(RecyclerView view) {
        super(view.getContext());        this.view = view;        this.overScrollMode = ViewCompat.getOverScrollMode(view);    }

    @SuppressWarnings("UnusedDeclaration")
    public MyLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
        super(view.getContext(), orientation, reverseLayout);        this.view = view;        this.overScrollMode = ViewCompat.getOverScrollMode(view);    }

    public void setOverScrollMode(int overScrollMode) {
        if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
            throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);        if (this.view == null) throw new IllegalStateException("view == null");        this.overScrollMode = overScrollMode;        ViewCompat.setOverScrollMode(view, overScrollMode);    }

    public static int makeUnspecifiedSpec() {
        return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);    }

    @Override    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);        final int heightMode = View.MeasureSpec.getMode(heightSpec);
        final int widthSize = View.MeasureSpec.getSize(widthSpec);        final int heightSize = View.MeasureSpec.getSize(heightSpec);
        final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;        final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;
        final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;        final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;
        final int unspecified = makeUnspecifiedSpec();
        if (exactWidth && exactHeight) {
            // in case of exact calculations for both dimensions let's use default "onMeasure" implementation            super.onMeasure(recycler, state, widthSpec, heightSpec);            return;        }

        final boolean vertical = getOrientation() == VERTICAL;
        initChildDimensions(widthSize, heightSize, vertical);
        int width = 0;        int height = 0;
        // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This        // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the        // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never        // called whiles scrolling)        recycler.clear();
        final int stateItemCount = state.getItemCount();        final int adapterItemCount = getItemCount();        // adapter always contains actual data while state might contain old data (f.e. data before the animation is        // done). As we want to measure the view with actual data we must use data from the adapter and not from  the        // state        for (int i = 0; i < adapterItemCount; i++) {
            if (vertical) {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items                        // we will use previously calculated dimensions                        measureChild(recycler, i, widthSize, unspecified, childDimensions);                    } else {
                        logMeasureWarning(i);                    }
                }
                height += childDimensions[CHILD_HEIGHT];                if (i == 0) {
                    width = childDimensions[CHILD_WIDTH];                }
                if (hasHeightSize && height >= heightSize) {
                    break;                }
            } else {
                if (!hasChildSize) {
                    if (i < stateItemCount) {
                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items                        // we will use previously calculated dimensions                        measureChild(recycler, i, unspecified, heightSize, childDimensions);                    } else {
                        logMeasureWarning(i);                    }
                }
                width += childDimensions[CHILD_WIDTH];                if (i == 0) {
                    height = childDimensions[CHILD_HEIGHT];                }
                if (hasWidthSize && width >= widthSize) {
                    break;                }
            }
        }

        if (exactWidth) {
            width = widthSize;        } else {
            width += getPaddingLeft() + getPaddingRight();            if (hasWidthSize) {
                width = Math.min(width, widthSize);            }
        }

        if (exactHeight) {
            height = heightSize;        } else {
            height += getPaddingTop() + getPaddingBottom();            if (hasHeightSize) {
                height = Math.min(height, heightSize);            }
        }

        setMeasuredDimension(width, height);
        if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
            final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                    || (!vertical && (!hasWidthSize || width < widthSize));
            ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);        }
    }

    private void logMeasureWarning(int child) {
        if (BuildConfig.DEBUG) {
            Log.w("MyLinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                    "To remove this message either use #setChildSize() method or don't run RecyclerView animations");        }
    }

    private void initChildDimensions(int width, int height, boolean vertical) {
        if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
            // already initialized, skipping            return;        }
        if (vertical) {
            childDimensions[CHILD_WIDTH] = width;            childDimensions[CHILD_HEIGHT] = childSize;        } else {
            childDimensions[CHILD_WIDTH] = childSize;            childDimensions[CHILD_HEIGHT] = height;        }
    }

    @Override    public void setOrientation(int orientation) {
        // might be called before the constructor of this class is called        //noinspection ConstantConditions        if (childDimensions != null) {
            if (getOrientation() != orientation) {
                childDimensions[CHILD_WIDTH] = 0;                childDimensions[CHILD_HEIGHT] = 0;            }
        }
        super.setOrientation(orientation);    }

    public void clearChildSize() {
        hasChildSize = false;        setChildSize(DEFAULT_CHILD_SIZE);    }

    public void setChildSize(int childSize) {
        hasChildSize = true;        if (this.childSize != childSize) {
            this.childSize = childSize;            requestLayout();        }
    }

    private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
        final View child;        try {
            child = recycler.getViewForPosition(position);        } catch (IndexOutOfBoundsException e) {
            if (BuildConfig.DEBUG) {
                Log.w("MyLinearLayoutManager", "MyLinearLayoutManager doesn't work well with animations. Consider switching them off", e);            }
            return;        }

        final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();
        final int hPadding = getPaddingLeft() + getPaddingRight();        final int vPadding = getPaddingTop() + getPaddingBottom();
        final int hMargin = p.leftMargin + p.rightMargin;        final int vMargin = p.topMargin + p.bottomMargin;
        // we must make insets dirty in order calculateItemDecorationsForChild to work        makeInsetsDirty(p);        // this method should be called before any getXxxDecorationXxx() methods        calculateItemDecorationsForChild(child, tmpRect);
        final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);        final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);
        final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());        final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());
        child.measure(childWidthSpec, childHeightSpec);
        dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;        dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;
        // as view is recycled let's not keep old measured values        makeInsetsDirty(p);        recycler.recycleView(child);    }

    private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
        if (!canMakeInsetsDirty) {
            return;        }
        try {
            if (insetsDirtyField == null) {
                insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");                insetsDirtyField.setAccessible(true);            }
            insetsDirtyField.set(p, true);        } catch (NoSuchFieldException e) {
            onMakeInsertDirtyFailed();        } catch (IllegalAccessException e) {
            onMakeInsertDirtyFailed();        }
    }

    private static void onMakeInsertDirtyFailed() {
        canMakeInsetsDirty = false;        if (BuildConfig.DEBUG) {
            Log.w("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");        }
    }
}




After that use this LayoutManager for your RecyclerView 

recyclerView.setLayoutManager(new MyLinearLayoutManager(getContext())); 

But you also should call those two methods: 
recyclerView.setNestedScrollingEnabled(false); 
recyclerView.setHasFixedSize(false);



Tuesday, 3 February 2015

RecyclerView in Android 5.0(Lollipop)

13:00:00 Posted by Kumanan ,

The RecyclerView is a new ViewGroup that is prepared to render any adapter-based view in a similar way. It is supossed to be the successor of ListView and GridView, and it can be found in the latest support-v7 version.

RecyclerView is the appropriate view to use when you have multiple items of the same type and it’s very likely that your user’s device cannot present all of those items at once. Possible examples are contacts, customers, audio files and so on. The user has to scroll up and down to see more items and that’s when the recycling and reuse comes into play. As soon as a user scrolls a currently visible item out of view, this item’s view can be recycled and reused whenever a new item comes into view.

If you want to use a RecyclerView, you will need to feel comfortable with three elements:
– RecyclerView.Adapter
– LayoutManager
– ItemAnimator

To download the complete source code click here.


First of all here’s the layout file containing the RecyclerView:

// activity_feefs_list.xml

<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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MyActivity">

    <view
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        class="android.support.v7.widget.RecyclerView"
        android:id="@+id/recycler_view"
        android:layout_alignParentStart="true" />
</RelativeLayout>


// list_row.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="2dp"
    android:id="@+id/assadas"
    android:weightSum="1">

    <ImageView
        android:id="@+id/thumbnail"
        android:layout_width="100dp"
        android:layout_height="90dp"
        android:layout_alignParentTop="true"
        android:layout_gravity="center_horizontal"
        android:scaleType="fitCenter"
        android:layout_marginLeft="5dp"
        android:src="@drawable/placeholder" />

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="233dp"
        android:layout_height="match_parent"
        android:id="@+id/textLayout"
        android:layout_weight="0.56">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_margin="8dp"
            android:text="Spring roll"
            android:textSize="15sp"
            android:textStyle="bold"
            android:maxLines="1" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Description Text"
            android:layout_marginLeft="8dp"
            android:id="@+id/description"
            android:maxLines="3"/>

    </LinearLayout>

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

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/imageView"
            android:background="@drawable/sendtokitchen"/>
    </LinearLayout>

</LinearLayout>


RecyclerView.Adapter

RecyclerView includes a new kind of adapter. It’s a similar approach to the ones you already used, but with some peculiarities, such as a required ViewHolder. You will have to override two main methods: one to inflate the view and its view holder, and another one to bind data to the view. The good thing about this is that first method is called only when we really need to create a new view. No need to check if it’s being recycled.

public class MyRecyclerAdapter extends RecyclerView.Adapter<FeedListRowHolder>{


    private List<FeedItem> feedItemList;

    private Context mContext;

    public MyRecyclerAdapter(Context context, List<FeedItem> feedItemList) {
        this.feedItemList = feedItemList;
        this.mContext = context;
    }

    @Override
    public FeedListRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_row, null);
        FeedListRowHolder mh = new FeedListRowHolder(v);
         return mh;
    }

    @Override
    public void onBindViewHolder(FeedListRowHolder feedListRowHolder, int i) {
        final FeedItem feedItem = feedItemList.get(i);

        Picasso.with(mContext).load(feedItem.getThumbnail())
                .error(R.drawable.placeholder)
                .placeholder(R.drawable.placeholder)
                .into(feedListRowHolder.thumbnail);

        feedListRowHolder.title.setText(Html.fromHtml(feedItem.getTitle()));
        feedListRowHolder.description.setText(feedItem.getDescription());

        feedListRowHolder.imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext,""+feedItem.getTitle(),Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return (null != feedItemList ? feedItemList.size() : 0);
    }
}

ViewHolder

ViewHolders are basically caches of your View objects. The Android team has been recommending using the ViewHolder pattern for a very long time, but they never actually enforced the use of it. Now with the new Adapter you finally have to use this pattern.

public class FeedListRowHolder extends RecyclerView.ViewHolder{
    protected ImageView thumbnail;
    protected TextView title;
    protected  TextView description;
    protected  ImageView imageView;

    public FeedListRowHolder(View view) {
        super(view);
        this.thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
        this.title = (TextView) view.findViewById(R.id.title);
        this.description = (TextView) view.findViewById(R.id.description);
        this.imageView = (ImageView) view.findViewById(R.id.imageView);
    }

}


RecyclerView.LayoutManager

The LayoutManager is probably the most interesting part of the RecyclerView. This class is responsible for the layout of all child views. There is one default implementation available: LinearLayoutManager which you can use for vertical as well as horizontal lists.

You have to set a LayoutManager for your RecyclerView otherwise you will see an exception at Runtime.

// if your not using LinearLayoutManager
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));


LinearlayoutManager

The implementation of LinearLayoutManager is rather complex and I only had a look at some key aspects. I will return to this implementation in my post about custom LayoutManagers.

To use the LinearLayoutManager you simply have to instantiate it, tell it which orientation to use and you are done:

LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.scrollToPosition(currPos);
recyclerView.setLayoutManager(layoutManager);

LinearLayoutManager also offers some methods to find out about the first and last items currently on screen:

- findFirstVisibleItemPosition()
- findFirstCompletelyVisibleItemPosition()
- findLastVisibleItemPosition()
- findLastCompletelyVisibleItemPosition()

RecyclerView.ItemAnimator

The ItemAnimator class helps the RecyclerView with animating individual items. ItemAnimators deal with three events:

An item gets added to the data set
An item gets removed from the data set
An item moves as a result of one or more of the previous two operations
Luckily there exists a default implementation aptly named DefaultItemAnimator. If you do not set a custom ItemAnimator, RecyclerView uses an instance of DefaultItemAnimator.

Obviously for animations to work, Android needs to know about changes to the dataset. For this Android needs the support of your adapter. In earlier versions of Android you would call notifyDataSetChanged() whenever changes occured, this is no longer appropriate. This method triggers a complete redraw of all (visible) children at once without any animation. To see animations you have to use more specific methods.

The RecyclerView.Adapter class contains plenty of notifyXyz() methods. The two most specific are:

public final void notifyItemInserted(int position)
public final void notifyItemRemoved(int position)


The Java code is also pretty simple:

public class FeedListActivity extends Activity {

    private static final String TAG = "RecyclerViewExample";

    private List<FeedItem> feedItemList = new ArrayList<FeedItem>();

    private RecyclerView mRecyclerView;

    private MyRecyclerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /* Allow activity to show indeterminate progressbar */
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

        setContentView(R.layout.activity_feeds_list);

        /* Initialize recyclerview */
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        /*Downloading data from below url*/
        final String url = "http://www.mocky.io/v2/54cf645364b65b3608610979";
        new AsyncHttpTask().execute(url);

    }

    public class AsyncHttpTask extends AsyncTask<String, Void, Integer> {

        @Override
        protected void onPreExecute() {
            setProgressBarIndeterminateVisibility(true);
        }

        @Override
        protected Integer doInBackground(String... params) {
            InputStream inputStream = null;
            Integer result = 0;
            HttpURLConnection urlConnection = null;

            try {
                /* forming th java.net.URL object */
                URL url = new URL(params[0]);

                urlConnection = (HttpURLConnection) url.openConnection();

                /* for Get request */
                urlConnection.setRequestMethod("GET");

                int statusCode = urlConnection.getResponseCode();

                /* 200 represents HTTP OK */
                if (statusCode ==  200) {

                    BufferedReader r = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = r.readLine()) != null) {
                        response.append(line);
                    }

                    parseResult(response.toString());
                    result = 1; // Successful
                }else{
                    result = 0; //"Failed to fetch data!";
                }

            } catch (Exception e) {
                Log.d(TAG, e.getLocalizedMessage());
            }

            return result; //"Failed to fetch data!";
        }

        @Override
        protected void onPostExecute(Integer result) {

            setProgressBarIndeterminateVisibility(false);

            /* Download complete. Lets update UI */
            if (result == 1) {
                adapter = new MyRecyclerAdapter(FeedListActivity.this, feedItemList);
                mRecyclerView.setAdapter(adapter);
            } else {
                Log.e(TAG, "Failed to fetch data!");
            }
        }
    }

    private void parseResult(String result) {
        try {
            JSONObject response = new JSONObject(result);
            JSONArray posts = response.optJSONArray("category");

            /*Initialize array if null*/
            if (null == feedItemList) {
                feedItemList = new ArrayList<FeedItem>();
            }

            for (int i = 0; i < posts.length(); i++) {
                JSONObject post = posts.optJSONObject(i);

                FeedItem item = new FeedItem();
                item.setTitle(post.optString("item_name"));
                item.setThumbnail(post.optString("item_image"));
                item.setDescription(post.optString("description"));
                feedItemList.add(item);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

}