A layout engine for Android that decouples layouts from the View containers that manage scrolling and view recycling. FreeFlow makes it really easy to create custom layouts and beautiful transition animations as data and layouts change

Last update: Aug 8, 2022

FreeFlow is no longer under development

We are no longer working on FreeFlow and have moved to RecyclerViews for similar functionality. Thanks for all your support and interest -Arpit Mathur

FreeFlow

A layout engine for Android that decouples layouts from the View containers that manage scrolling and view recycling. FreeFlow makes it really easy to create custom layouts and beautiful transition animations as layouts are changed.

FreeFlow is a composition based approach to Android Layouts. As opposed to default Android Layouts, FreeFlow Layouts are swappable at runtime which allows views to their new states smoothly. The fundamental difference here is that FreeFlow prefers Composition over Inheritance which makes the system a lot more adaptable.

Freeflow may be considered in "alpha". You can help in many ways, by reviewing and making suggestions on api's to actually finding bugs and submitting patches via pull requests.

FreeFlow is inspired by UI frameworks like UICollectionViews on iOS and the Spark Architecture in Flex.

Building Blocks

At the basic level, FreeFlow consists of 4 parts:

  • FreeFlowContainer: The core class that extends ViewGroup and places all Views inside it
  • FreeFlowLayout: The class thats responsible for defining the Rect's that the Container will use to position the Views.
  • FreeFlowLayoutAnimator: The animator that will animate the Views when their position Rect's are changed because of a change in the Layout
  • SectionedAdapter: The data adapter class that returns the View instances based on the data being rendered. Its modeled very closely to the List Adapters that are used in Android but also understand the concept of "Section" which might segment data into different parts (For example a user's contacts list may include Sections that hold names beginning with a particular character).

Additionally there are some helper classes like the DefaultLayoutAnimator that will transition views automatically as they get added, moved or removed and is pretty configurable. FreeFlow comes with some basic Layouts like HLayout, VLayout , HGridLayout and VGridLayout but its easy enough to create custom layouts (see the Artbook example's custom layout)

The Artbook example in this repository is a good example of whats possible with FreeFlow. You can download the .apk from the releases tab or see the experience on the video below:

Artbook demo project

Feedback/Support/Help

Join the Google+ community for questions or keeping up with upcoming features, releases, etc

If you have changes you'd like to commit to the repo, you will need to sign the [Comcast Contributor License Agreement](Comcast Contributor License Agreement (03-07-14).pdf) available at the root of this repository.

GitHub

https://github.com/Comcast/FreeFlow
Comments
  • 1. View clipping occurs after scrolling horizontally

    I created a ViewGroup that extends RelativeLayout and adds support for rounded corners which you can find here: https://gist.github.com/jerrellmardis/9240278. This ViewGroup is used to display each cell in the grid. When the container initially displays all cells have nice rounded corners but as you scroll back and forth the some of the corners on the cells get clipped. Here's a couple screenshots to illustrate what I'm talking about.

    Expected: https://dl.dropboxusercontent.com/u/93908706/Screenshot_2014-02-28-17-26-36.png

    Actual: https://dl.dropboxusercontent.com/u/93908706/Screenshot_2014-02-28-17-26-55.png

    The views that are affected seem a bit random. Here's my layout as well.

    public class QuiltLayout extends FreeFlowLayoutBase implements FreeFlowLayout {
    
        private Context mContext;
        private HashMap<Object, FreeFlowItem> mProxies;
        private int mLargeItemSide;
        private int mRegularItemSide;
    
        public QuiltLayout(Context context) {
            mContext = context;
            mProxies = new HashMap<Object, FreeFlowItem>();
        }
    
        @Override
        public void setDimensions(int measuredWidth, int measuredHeight) {
            super.setDimensions(measuredWidth, measuredHeight);
            mRegularItemSide = mContext.getResources().getDimensionPixelSize(R.dimen.patch_width_1);
            mLargeItemSide = mContext.getResources().getDimensionPixelSize(R.dimen.patch_width_2);
        }
    
        @Override
        public void prepareLayout() {
            mProxies.clear();
    
            Section section = itemsAdapter.getSection(0);
    
            final float regularHeightHRatio = .4f;
            final float largeHeightHRatio = .6f;
            int h1 = Math.round(height * regularHeightHRatio);
            int h2 = Math.round(height * largeHeightHRatio);
            int xOffset = 0;
    
            for (int i = 0; i < section.getDataCount(); i++) {
                FreeFlowItem freeFlowItem = new FreeFlowItem();
                freeFlowItem.isHeader = false;
                freeFlowItem.itemIndex = i;
                freeFlowItem.itemSection = 0;
                freeFlowItem.data = section.getDataAtIndex(i);
    
                Rect r = new Rect();
    
                switch (i % 18) {
                    case 0:
                        r.left = 0;
                        r.top = 0;
                        r.right = mLargeItemSide;
                        r.bottom = height;
                        break;
                    case 1:
                        r.left = 2 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = h1;
                        break;
                    case 2:
                        r.left = 2 * mRegularItemSide;
                        r.top = h1;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = height;
                        break;
                    case 3:
                        r.left = 3 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mLargeItemSide;
                        r.bottom = h2;
                        break;
                    case 4:
                        r.left = 3 * mRegularItemSide;
                        r.top = h2;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = height;
                        break;
                    case 5:
                        r.left = 4 * mRegularItemSide;
                        r.top = h2;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = height;
                        break;
                    case 6:
                        r.left = 5 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = h1;
                        break;
                    case 7:
                        r.left = 5 * mRegularItemSide;
                        r.top = h1;
                        r.right = r.left + mLargeItemSide;
                        r.bottom = height;
                        break;
                    case 8:
                        r.left = 6 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = h1;
                        break;
                    case 9:
                        r.left = 7 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mLargeItemSide;
                        r.bottom = height;
                        break;
                    case 10:
                        r.left = 9 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = h2;
                        break;
                    case 11:
                        r.left = 9 * mRegularItemSide;
                        r.top = h2;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = height;
                        break;
                    case 12:
                        r.left = 10 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = h1;
                        break;
                    case 13:
                        r.left = 10 * mRegularItemSide;
                        r.top = h1;
                        r.right = r.left + mLargeItemSide;
                        r.bottom = height;
                        break;
                    case 14:
                        r.left = 11 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = h1;
                        break;
                    case 15:
                        r.left = 12 * mRegularItemSide;
                        r.top = 0;
                        r.right = r.left + mLargeItemSide;
                        r.bottom = h2;
                        break;
                    case 16:
                        r.left = 12 * mRegularItemSide;
                        r.top = h2;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = height;
                        break;
                    case 17:
                        r.left = 13 * mRegularItemSide;
                        r.top = h2;
                        r.right = r.left + mRegularItemSide;
                        r.bottom = height;
                        break;
                }
    
                r.offset(xOffset, 0);
                freeFlowItem.frame = r;
                mProxies.put(section.getDataAtIndex(i), freeFlowItem);
    
                if (i % 18 == 17) xOffset += 14 * mRegularItemSide;
            }
        }
    
        @Override
        public HashMap<Object, FreeFlowItem> getItemProxies(int viewPortLeft, int viewPortTop) {
            Rect viewport = new Rect(viewPortLeft, viewPortTop, viewPortLeft + width, viewPortTop + height);
            HashMap<Object, FreeFlowItem> ret = new HashMap<Object, FreeFlowItem>();
    
            for (Map.Entry<Object, FreeFlowItem> entry : mProxies.entrySet()) {
                FreeFlowItem p = entry.getValue();
                if (Rect.intersects(p.frame, viewport)) {
                    ret.put(entry.getKey(), p);
                }
            }
    
            return ret;
    
        }
    
        @Override
        public FreeFlowItem getFreeFlowItemForItem(Object item) {
            return mProxies.get(item);
        }
    
        @Override
        public int getContentWidth() {
            if (itemsAdapter == null)
                return 0;
    
            int sectionIndex = itemsAdapter.getNumberOfSections() - 1;
            Section s = itemsAdapter.getSection(sectionIndex);
    
            if (s.getDataCount() == 0)
                return 0;
    
            Object lastFrameData = s.getDataAtIndex(s.getDataCount() - 1);
            FreeFlowItem fd = mProxies.get(lastFrameData);
    
            return fd.frame.left + fd.frame.width();
        }
    
        @Override
        public int getContentHeight() {
            return height;
        }
    
        @Override
        public FreeFlowItem getItemAt(float x, float y) {
            return ViewUtils.getItemAt(mProxies, (int) x, (int) y);
        }
    
        @Override
        public boolean verticalScrollEnabled() {
            return false;
        }
    
        @Override
        public boolean horizontalScrollEnabled() {
            return true;
        }
    
        @Override
        public void setLayoutParams(FreeFlowLayoutParams params){ }
    }
    
    Reviewed by jerrellmardis at 2014-02-28 23:38
  • 2. Fix crash when getScaledOverflingDistance() == 0

    We encounted crash on calculation of edge effects. Divide by zero on below line.

    mLeftEdge.onPull(viewPortX / (-overflingDistance));
    

    http://crashes.to/s/1f8091cd688

    N-03E, device sold in Japan with "Disney Mobile on docomo" brand, seems to return 0 for connfiguration.getScaledOverflingDistance() This can be done with customizing this constant of ViewConfiguration: http://tools.oesf.biz/android-4.0.4_r1.0/xref/frameworks/base/core/java/android/view/ViewConfiguration.java#224 .

    Possible fix was on https://github.com/Comcast/FreeFlow/commit/003e8fc486f567b42881bec5770c2384c73d3924#diff-e7d010dace6714a20e845b07ba789fa6R1027 , but it was removed on https://github.com/Comcast/FreeFlow/commit/60c56fe0e9f644f979d84ebcdd190a042658257c#diff-c38b0300174165db5f03f319b3a71847L1122 .

    Thanks!

    Reviewed by ypresto at 2015-04-09 11:38
  • 3. Horizontal Scroll broken?

    Setting horizontalScrollEnabled() to false and setting the appropriate content width in getContentWidth() does not produce the desired effects when extending FreeFlowLayoutBase.

    Reviewed by jerrellmardis at 2014-02-28 17:34
  • 4. Click triggered unnecessarily

    I have implemented OnItemClickListener for the FreeFlowContainer, and I have added few items into the container, both scroll container and clicking on the item working good, but the issue is when I drag down from the first item the click event fired, also when I drag up the last item, the click event fired.

    Reviewed by kbala at 2014-06-17 15:51
  • 5. dynamic height - FreeFlowItem

    Hi, I am using freeflowcontainer with VLayout, each item having an Image and caption under that, the height of the caption textview vary based on the amount of text populated into that, So I want to extend the height of each freeflowitem based on caption height, please help, thanks

    Reviewed by kbala at 2014-06-09 20:09
  • 6. Added Gradle wrapper

    I've added the gradle wrapper and other necessary config files. It's been tested with both Android Studio and Eclipse and working well. (I just sent my signed CLA email :smile: )

    Reviewed by yelinaung at 2014-04-16 21:38
  • 7. Offscreen buffer

    There is no offscreen buffer at the moment. A concrete example of where this can be seen is when an imageview that contains a network image moves off screen by just a little bit, it has to reload the image every time it comes back on screen.

    I think we should add this functionality to the container class, possibly as a pixel count buffer, or row/column count buffer. The problem with row/column count is that that only makes sense for rectangular layouts (something that is not a requirement).

    Reviewed by rratmansky at 2013-12-03 16:28
  • 8. Vertical Scroll is returning null

    I am trying to implement the library but I got my head stuck on this issue can you please point me out what I am doing wrong ?

    That's the debug info

    03-07 11:29:00.979    1755-1755/com.project.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
        Process: com.project.app, PID: 1755
        java.lang.NullPointerException
                at com.comcast.freeflow.core.FreeFlowContainer.getViewChanges(FreeFlowContainer.java:615)
                at com.comcast.freeflow.core.FreeFlowContainer.moveViewport(FreeFlowContainer.java:1064)
                at com.comcast.freeflow.core.FreeFlowContainer.moveViewportBy(FreeFlowContainer.java:1004)
                at com.comcast.freeflow.core.FreeFlowContainer.onTouchEvent(FreeFlowContainer.java:848)
                at android.view.View.dispatchTouchEvent(View.java:7706)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2210)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1945)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
                at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
                at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
                at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2068)
                at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1515)
                at android.app.Activity.dispatchTouchEvent(Activity.java:2458)
                at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2016)
                at android.view.View.dispatchPointerEvent(View.java:7886)
                at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3954)
                at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3833)
                at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3399)
                at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3449)
                at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3418)
                at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3525)
                at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3426)
                at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3582)
                at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3399)
                at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3449)
                at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3418)
                at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3426)
                at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3399)
                at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5602)
                at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5582)
                at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5553)
                at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5682)
                at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
                at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
                at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176)
                at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:5655)
                at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:5701)
                at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
                at android.view.Choreographer.doCallbacks(Choreographer.java:574)
                at android.view.Choreographer.doFrame(Choreographer.java:542)
                at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
                at android.os.Handler.handleCallback(Handler.java:733)
                at android.os.Handler.dispat
    

    Here is my Fragment

    package com.project.app.fragments;
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Point;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.view.Display;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    
    import com.comcast.freeflow.core.AbsLayoutContainer;
    import com.comcast.freeflow.core.FreeFlowContainer;
    import com.comcast.freeflow.core.FreeFlowItem;
    import com.comcast.freeflow.core.Section;
    import com.comcast.freeflow.core.SectionedAdapter;
    import com.comcast.freeflow.layouts.FreeFlowLayout;
    import com.comcast.freeflow.layouts.VGridLayout;
    import com.comcast.freeflow.layouts.VLayout;
    import com.koushikdutta.urlimageviewhelper.UrlImageViewHelper;
    import com.loopj.android.http.AsyncHttpClient;
    import com.loopj.android.http.JsonHttpResponseHandler;
    import com.loopj.android.http.RequestParams;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import de.keyboardsurfer.android.widget.crouton.Configuration;
    import de.keyboardsurfer.android.widget.crouton.Crouton;
    import de.keyboardsurfer.android.widget.crouton.Style;
    import com.project.app.R;
    import com.project.app.models.Event;
    import com.project.app.models.Photo;
    import com.project.app.models.User;
    import com.project.app.models.Venue;
    import com.project.app.ui.ArtbookLayout;
    import com.project.app.utils.Fields;
    
    public class VenueGalleryFragment extends Fragment {
        Activity venueActivity;
        FreeFlowContainer ffContainer;
        ArtbookLayout custom;
        FreeFlowLayout[] layouts;
        VenuePhotosListAdapter adapter;
        Venue venue;
        AsyncHttpClient client;
        RequestParams photosParams;
        User currentUser;
        List<Photo> photos = new ArrayList<Photo>();
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_venue_gallery, container, false);
            venue = (Venue) venueActivity.getIntent().getExtras().getSerializable("venue");
    
            ffContainer = (FreeFlowContainer) rootView.findViewById(R.id.ffcGallery);
            Display display = venueActivity.getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
    
            custom = new ArtbookLayout();
            layouts = new FreeFlowLayout[]{custom};
    
            // Get the images
            client = new AsyncHttpClient();
            client.setTimeout(Fields.DEFAULT_TIMEOUT);
            currentUser = User.currentUser();
            photosParams = new RequestParams();
    
            adapter = new VenuePhotosListAdapter(venueActivity);
            ffContainer.setLayout(layouts[0]);
            ffContainer.setAdapter(adapter);
    
            client.get(Fields.GRAPH_VENUES_URL + "/" + venue.venueId + "/photos", photosParams, new JsonHttpResponseHandler() {
                @Override
                public void onSuccess(int statusCode, JSONObject response) {
                    venueActivity.setProgressBarIndeterminateVisibility(Boolean.FALSE);
                    try {
                        JSONArray photosArray = response.getJSONArray("photos");
                        for (int i = 0; i < photosArray.length(); i++) {
                            JSONObject photo = photosArray.getJSONObject(i);
                            photos.add(Photo.fromJson(photo));
                        }
    
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    adapter.update(photos);
                    ffContainer.dataInvalidated();
                    ffContainer.setOnItemClickListener(new AbsLayoutContainer.OnItemClickListener() {
                        @Override
                        public void onItemClick(AbsLayoutContainer parent, FreeFlowItem proxy) {
    
                        }
                    });
    
                    ffContainer.addScrollListener( new FreeFlowContainer.OnScrollListener() {
    
                        @Override
                        public void onScroll(FreeFlowContainer container) {
                        }
                    });
                }
            });
    
            return rootView;
        }
    
        protected class VenuePhotosListAdapter implements SectionedAdapter {
    
            private Context context;
            private Section section;
    
            public VenuePhotosListAdapter(Context context) {
                this.context = context;
                section = new Section();
                section.setSectionTitle("Pics");
            }
    
            public void update(List<Photo> photos){
    
                for(Photo o : photos){
                    section.getData().add(o);
                }
            }
            @Override
            public long getItemId(int section, int position) {
                return section * 1000 + position;
            }
    
            @Override
            public View getItemView(int sectionIndex, int position, View convertView, ViewGroup parent) {
                if (convertView == null) {
                    convertView = LayoutInflater.from(context).inflate(R.layout.list_item_pic_view, parent, false);
                }
                ImageView img = (ImageView) convertView.findViewById(R.id.pic);
                Photo p = (Photo) (section.getData().get(position));
                UrlImageViewHelper.setUrlDrawable(img, p.imageUrl, getResources().getDrawable(R.drawable.logo));
                return convertView;
            }
    
            @Override
            public View getHeaderViewForSection(int section, View convertView,
                                                ViewGroup parent) {
                return null;
            }
    
            @Override
            public int getNumberOfSections() {
                if(section.getData().size() == 0) return 0;
                return 1;
            }
    
            @Override
            public Section getSection(int index) {
                return section;
            }
    
            @Override
            public Class[] getViewTypes() {
                return new Class[] { LinearLayout.class };
            }
    
            @Override
            public Class getViewType(FreeFlowItem proxy) {
                return LinearLayout.class;
            }
    
            @Override
            public boolean shouldDisplaySectionHeaders() {
                return false;
            }
    
        }
    
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            venueActivity = activity;
        }
    
    }
    
    
    Reviewed by tagrudev at 2014-03-07 09:38
  • 9. Removed setAlpha() calls which can be potentially expensive during scrolling

    The setAlpha() operation is optimized in certain circumstances by the framework which helps during scrolling. This should be removed since the container cannot assume that the setAlpha() call will be optimized by the framework for the given view.

    Reviewed by jerrellmardis at 2014-03-24 07:02
  • 10. Pulling the items already and the edge and then changing direction of pull will prevent the List from bouncing back up

    • With a Container with a VLayout and vertical scroll position already set to 0, pull down.
    • A small gap appears on top
    • Without lifting your finger, change the scroll direction to horizontal.
    • Lift up the finger
    • Bug: The gap remains (List doesn't bounce back)
    Reviewed by arpit at 2014-03-16 03:48
  • 11. Setting a layout and immidiately invalidating the data will not recompute scroll

    Steps:

    • set a new layout
    • call data invalidated in next line
    • shouldRecalculateScrollWhenComputingLayout will be set to false even though it should recalculate the viewport because of layout change
    Reviewed by arpit at 2014-03-16 03:41
  • 12. view item click

    Hi! I have noticed that if i set a clicklistener on a element inside the view the scroll is blocked. Is possibile to propagate the touch to the flowcontainer????

    Reviewed by FunnyDevs at 2016-01-09 18:49
  • 13. This library causes UNEXPECTED TOP-LEVEL EXCEPTION error

    Hi,

    I imported FreeFlow using jitpack and everything on building is okay but while I'm trying to debug my app UNEXPECTED TOP-LEVEL EXCEPTION will be thrown.

    Information:Gradle tasks [:app:assembleDebug]
    :app:preBuild UP-TO-DATE
    :app:preDebugBuild UP-TO-DATE
    :app:checkDebugManifest
    :app:preReleaseBuild UP-TO-DATE
    :app:prepareComAfollestadMaterialDialogs0781Library UP-TO-DATE
    :app:prepareComAndroidSupportAppcompatV72221Library UP-TO-DATE
    :app:prepareComAndroidSupportDesign2221Library UP-TO-DATE
    :app:prepareComAndroidSupportRecyclerviewV72221Library UP-TO-DATE
    :app:prepareComAndroidSupportSupportV42221Library UP-TO-DATE
    :app:prepareComDaimajiaSliderLibrary115Library UP-TO-DATE
    :app:prepareComGithubComcastFreeFlowV06Library UP-TO-DATE
    :app:prepareComGoogleAndroidGmsPlayServicesBase750Library UP-TO-DATE
    :app:prepareComGoogleAndroidGmsPlayServicesLocation750Library UP-TO-DATE
    :app:prepareComGoogleAndroidGmsPlayServicesMaps750Library UP-TO-DATE
    :app:prepareComOrhanobutHawk118Library UP-TO-DATE
    :app:prepareComSoundcloudAndroidAndroidCrop100Library UP-TO-DATE
    :app:prepareDeHdodenhofCircleimageview130Library UP-TO-DATE
    :app:prepareIoReactivexRxandroid101Library UP-TO-DATE
    :app:prepareMeGujunAndroidTaggroupLibrary14Library UP-TO-DATE
    :app:preparePlCharmasAndroidAndroidReactiveLocation07Library UP-TO-DATE
    :app:prepareDebugDependencies
    :app:compileDebugAidl UP-TO-DATE
    :app:compileDebugRenderscript UP-TO-DATE
    :app:generateDebugBuildConfig UP-TO-DATE
    :app:generateDebugAssets UP-TO-DATE
    :app:mergeDebugAssets UP-TO-DATE
    :app:generateDebugResValues UP-TO-DATE
    :app:generateDebugResources UP-TO-DATE
    :app:mergeDebugResources UP-TO-DATE
    :app:processDebugManifest UP-TO-DATE
    :app:processDebugResources UP-TO-DATE
    :app:generateDebugSources UP-TO-DATE
    :app:processDebugJavaRes UP-TO-DATE
    :app:compileDebugJava UP-TO-DATE
    :app:compileDebugNdk UP-TO-DATE
    :app:compileDebugSources UP-TO-DATE
    :app:preDexDebug UP-TO-DATE
    :app:dexDebug
    UNEXPECTED TOP-LEVEL EXCEPTION:
    com.android.dex.DexException: Multiple dex files define Landroid/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs;
        at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:596)
        at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:554)
        at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:535)
        at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:171)
        at com.android.dx.merge.DexMerger.merge(DexMerger.java:189)
        at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:454)
        at com.android.dx.command.dexer.Main.runMonoDex(Main.java:303)
        at com.android.dx.command.dexer.Main.run(Main.java:246)
        at com.android.dx.command.dexer.Main.main(Main.java:215)
        at com.android.dx.command.Main.main(Main.java:106)
    Error:Execution failed for task ':app:dexDebug'.
    > com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Program Files\Java\jdk1.7.0_80\bin\java.exe'' finished with non-zero exit value 2
    Information:BUILD FAILED
    Information:Total time: 8.212 secs
    Information:1 error
    Information:0 warnings
    Information:See complete output in console
    

    My current dependencies are

    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
    
        compile 'com.android.support:support-v4:22.2.1'
        compile 'com.android.support:design:22.2.1'
        compile 'com.android.support:appcompat-v7:22.2.1'
        compile 'com.loopj.android:android-async-http:1.4.8'
        compile 'com.soundcloud.android:android-crop:[email protected]'
        compile 'pl.charmas.android:android-reactive-location:[email protected]'
        compile 'com.google.android.gms:play-services-location:7.5.0'
        compile 'io.reactivex:rxandroid:1.0.1'
        compile 'com.squareup.picasso:picasso:2.5.2'
        compile 'com.nineoldandroids:library:2.4.0'
        compile 'com.daimajia.slider:library:[email protected]'
        compile 'com.orhanobut:hawk:1.18'
        compile 'com.mobsandgeeks:android-saripaar:2.0.2'
        compile 'de.hdodenhof:circleimageview:1.3.0'
        compile 'com.afollestad:material-dialogs:0.7.8.1'
        compile 'me.gujun.android.taggroup:library:[email protected]'
        /*compile 'uk.co.ribot:easyadapter:[email protected]'*/
        compile ('com.github.Comcast:FreeFlow:v-0.6'){
            exclude group: "com.android.support", module: "support-v4"
        }
    }
    

    I tried to exclude com.android.support library from the library but not worked. How can I fix this?

    Reviewed by meness at 2015-08-18 08:08
  • 14. Recursively computing layout when RelativeLayout and android:layout_above

    I found memory is growing and GC is running when FreeFlow is shown even if I didn't touch to the device. Allocation tracking says FreeFlow is computing layout constantly.

    image

    It was because FreeFlowContainer is placed in RelativeLayout and layout_above is used. (I placed LinearLayout with fixed height of 76dp, which contains Buttons, on the bottom of fragment.) For workaround I applied below diff to my xml file.

        <FreeFlowContainer
             android:layout_width="match_parent"
    -        android:layout_height="wrap_content"
    +        android:layout_height="match_parent"
             android:layout_alignParentTop="true"
    -        android:layout_above="@id/footerButton"
    +        android:layout_alignParentBottom="true"
    +        android:layout_marginBottom="76dp"
    

    This issue also causes bug that sometimes onItemSelected() is not called.

    Reviewed by ypresto at 2015-07-03 03:18
  • 15. Removed view remains short while when animation is canceled

    https://github.com/Comcast/FreeFlow/blob/96ebb0cc959ba0bfe5d8a8118aa9345550ff58c9/FreeFlow/src/com/comcast/freeflow/animations/DefaultLayoutAnimator.java#L108

    Reviewed by ypresto at 2015-06-18 10:24
  • 16. Invalid index is passed to adapter when notifyDataSetChanged() while scrolling

    FreeFlowContainer#notifyDataSetChanged() calls requestLayout() and redraw with backed adapter (computeLayout() and mLayout.prepareLayout()) in onMeasure().

    • https://github.com/Comcast/FreeFlow/blob/96ebb0cc959ba0bfe5d8a8118aa9345550ff58c9/FreeFlow/src/com/comcast/freeflow/core/FreeFlowContainer.java#L276
    • https://github.com/Comcast/FreeFlow/blob/96ebb0cc959ba0bfe5d8a8118aa9345550ff58c9/FreeFlow/src/com/comcast/freeflow/core/FreeFlowContainer.java#L255

    But when requestLayout() is called while scrolling, onTouchEvent() is called before onMeasure() and it causes crash by IndexOutOfBoundsException in adapter implementation.

    Note that ListView effectively avoids this problem by call layoutChildren() before applying scroll to view.

    1. Set mDataChanged = true in notifyDataSetChanged(). https://github.com/android/platform_frameworks_base/blob/android-5.1.1_r4/core/java/android/widget/AdapterView.java#L809
    2. Check mDataChanged and call layoutChildren() inTouchMove(). https://github.com/android/platform_frameworks_base/blob/android-5.1.1_r4/core/java/android/widget/AbsListView.java#L3763
    3. Then redraw views with new data from mAdapter. https://github.com/android/platform_frameworks_base/blob/android-5.1.1_r4/core/java/android/widget/ListView.java#L1561
    Reviewed by ypresto at 2015-06-18 08:46
Related tags
A pull to refresh layout for android, the RecyclerRefreshLayout is based on the SwipeRefreshLayout. support all the views, highly customizable, code simplicity, etc. really a practical RefreshLayout!
A pull to refresh layout for android, the RecyclerRefreshLayout is based on the SwipeRefreshLayout. support all the views, highly customizable, code simplicity, etc.  really a practical RefreshLayout!

RecyclerRefreshLayout English | 中文版 RecyclerRefreshLayout based on the {@link android.support.v4.widget.SwipeRefreshLayout} The RecyclerRefreshLayout

Aug 17, 2022
FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout
FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout

FixedHeaderTableLayout is a powerful Android library for displaying complex data structures and rendering tabular data composed of rows, columns and cells with scrolling and zooming features. FixedHeaderTableLayout is similar in construction and use as to Android's TableLayout

Aug 11, 2022
A layout to transition between two views using a Floating Action Button as shown in many Material Design concepts
A layout to transition between two views using a Floating Action Button as shown in many Material Design concepts

⚠ This library is no longer maintained ⚠️ FABRevealLayout A layout to transition between two views using a Floating Action Button as shown in many Mat

Jul 22, 2022
A custom ViewPager title strip which gives continuous feedback to the user when scrolling
A custom ViewPager title strip which gives continuous feedback to the user when scrolling

SmartTabLayout A custom ViewPager title strip which gives continuous feedback to the user when scrolling. This library has been added some features an

Aug 14, 2022
Allows the easy creation of animated transition effects when the state of Android UI has changed
Allows the easy creation of animated transition effects when the state of Android UI has changed

android-transition Android-Transition allows the easy creation of view transitions that reacts to user inputs. The library is designed to be general e

Aug 7, 2022
Repositório para criar layouts e chamar na activity main e melhorar um dos pontos fracos meu (layout).
 Repositório para criar layouts e chamar na activity main e melhorar um dos pontos fracos meu (layout).

Repositório para criar layouts e chamar na activity main e melhorar um dos pontos fracos meu (layout). Não se preocupe com os tipos malucos de layouts

Dec 14, 2021
Responsive Layout Gird Configuration using Compose. An adaptive layout
Responsive Layout Gird Configuration using Compose. An adaptive layout

ResponsiveGrid Responsive Grid is most followed layout system by the designer as it adapts to screen size and orientation, ensuring consistency across

Apr 12, 2022
ViewStateLayout - Easy way to manage common state templates like loading, empty, error etc.!
ViewStateLayout - Easy way to manage common state templates like loading, empty, error etc.!

ViewStateLayout Easy way to manage common state templates like loading, empty, error etc.! How to Step 1. Add the JitPack repository to your build fil

Jul 17, 2022
a custom pull-to-refresh layout which contains a interesting animation
a custom pull-to-refresh layout which contains a interesting animation

This is a project with custom pull-to-refresh layout which contains a interesting animation. And the animation is inspired by https://dribbble.com/sho

Jul 27, 2022
Easy, flexible and powerful Swipe Layout for Android
Easy, flexible and powerful Swipe Layout for Android

SwipeRevealLayout A layout that you can swipe/slide to show another layout. Demo Overview Drag mode Drag mode normal: Drag mode same_level: Features F

Aug 11, 2022
A 3D Layout for Android,When you use it warp other view,it can became a 3D view,一秒让你的view拥有3D效果!
A 3D Layout for Android,When you use it warp other view,it can became a 3D view,一秒让你的view拥有3D效果!

ThreeDLayout A 3D Layout,When you use it warp other view,it can became a 3D view 中文文档 preview USAGE 1.compile library allprojects { repositories {

Jun 7, 2022
A library for showing different types of layouts when a list view is empty
A library for showing different types of layouts when a list view is empty

Android Empty Layout Please note that this project is not being maintained now. Hopefully a new version will be available soon. A library for showing

Aug 8, 2022
NodeFlow is a library that makes visualizing hierarchical content easier.
NodeFlow is a library that makes visualizing hierarchical content easier.

NodeFlow NodeFlow is an Android library that provides a simple way to visualize hierarchical content. Perfect for displaying items that are organized

Dec 7, 2021
Implementation of ExpandableListview with custom header and custom content.
Implementation of ExpandableListview with custom header and custom content.

ExpandableLayout ExpandableLayout provides an easy way to create a view called header with an expandable view. Both view are external layout to allow

Aug 15, 2022
Material Design Search View Layout, now implemented in Google Maps, Dialer, etc

THIS PROJECT IS DEPRECATED Component is not maintained anymore. Implementation of Lollipop+ Dialer and Google Maps. DEMO Add in View Add to your layou

Jun 29, 2022
A library that easily allows you to mask layouts/viewgroups
A library that easily allows you to mask layouts/viewgroups

Maskable Layout Overview ======================= The Maskable Layout is a simple framelayout that allows you to easily mask views and viewgroups. You

Aug 16, 2022
A beautiful leanback port for Smartphones and Tablets
A beautiful leanback port for Smartphones and Tablets

MaterialLeanBack A beautiful leanback port for Smartphones and Tablets Sample Usage In your layout <com.github.florent37.materialleanback.MaterialLean

Aug 2, 2022
This component like SwipeRefreshLayout, it is more beautiful than SwipeRefreshLayout.
This component like SwipeRefreshLayout, it is more beautiful than SwipeRefreshLayout.

android-PullRefreshLayout This component like SwipeRefreshLayout, it is more beautiful than SwipeRefreshLayout. Demo Usage Add dependency. dependencie

Aug 2, 2022