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

Related tags

Layout FreeFlow
Overview

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.

Comments
  • View clipping occurs after scrolling horizontally

    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){ }
    }
    
    opened by jerrellmardis 11
  • Fix crash when getScaledOverflingDistance() == 0

    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!

    opened by ypresto 7
  • Horizontal Scroll broken?

    Horizontal Scroll broken?

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

    opened by jerrellmardis 6
  • Click triggered unnecessarily

    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.

    opened by kbala 5
  • dynamic height - FreeFlowItem

    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

    opened by kbala 4
  • Added Gradle wrapper

    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: )

    opened by yelinaung 4
  • Offscreen buffer

    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).

    opened by rratmansky 3
  • Vertical Scroll is returning null

    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;
        }
    
    }
    
    
    question 
    opened by tagrudev 2
  • Removed setAlpha() calls which can be potentially expensive during scrolling

    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.

    opened by jerrellmardis 1
  • Pulling the items already and the edge and then changing direction of pull will prevent the List from bouncing back up

    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)
    bug 
    opened by arpit 1
  • Setting a layout and immidiately invalidating the data will not recompute scroll

    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
    bug 
    opened by arpit 1
  • view item click

    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????

    opened by FunnyDevs 0
  • This library causes UNEXPECTED TOP-LEVEL EXCEPTION error

    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:1.0.0@aar'
        compile 'pl.charmas.android:android-reactive-location:0.7@aar'
        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:1.1.5@aar'
        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:1.4@aar'
        /*compile 'uk.co.ribot:easyadapter:1.5.0@aar'*/
        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?

    opened by meness 1
  • Recursively computing layout when RelativeLayout and android:layout_above

    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.

    opened by ypresto 0
  • Removed view remains short while when animation is canceled

    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

    opened by ypresto 0
  • Invalid index is passed to adapter when notifyDataSetChanged() while scrolling

    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
    opened by ypresto 2
Releases(v-0.6)
Owner
Comcast brings together the best in media and technology to create the world's best entertainment and online experiences
null
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

dinus_developer 1.7k Nov 10, 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

null 33 Dec 8, 2022
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

Tomás Ruiz-López 901 Dec 9, 2022
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

ogaclejapan 7k Jan 7, 2023
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

Kai 615 Nov 14, 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). Não se preocupe com os tipos malucos de layouts

Murillo Alves da Silva 1 Dec 14, 2021
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

null 4 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.! How to Step 1. Add the JitPack repository to your build fil

Kamrul Hasan 7 Dec 15, 2022
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

ZhangLei 1.8k Dec 27, 2022
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

Chau Thai 1.5k Jan 4, 2023
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 {

androidwing 490 Oct 27, 2022
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

Raquib-ul Alam (Kanak) 606 Nov 26, 2022
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

Telenav Inc 546 Dec 7, 2021
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

Robin Chutaux 1.6k Dec 12, 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

Sahil Dave 1.1k Dec 22, 2022
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

Christophe Smet 654 Dec 2, 2022
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

Florent CHAMPIGNY 739 Aug 2, 2022
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

星一 2.1k Dec 3, 2022