Skip to content

Commit

Permalink
PoC for databinding of data set.
Browse files Browse the repository at this point in the history
  • Loading branch information
dozd authored and Dolezal Zdenek committed Nov 3, 2016
1 parent 143049d commit bb88602
Show file tree
Hide file tree
Showing 10 changed files with 456 additions and 1 deletion.
4 changes: 4 additions & 0 deletions flexible-adapter-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ android {
testOptions {
unitTests.returnDefaultValues = true
}

dataBinding {
enabled = true
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import eu.davidea.samples.flexibleadapter.fragments.AbstractFragment;
import eu.davidea.samples.flexibleadapter.fragments.FragmentAnimators;
import eu.davidea.samples.flexibleadapter.fragments.FragmentAsyncFilter;
import eu.davidea.samples.flexibleadapter.fragments.FragmentDataBindingHeadersSections;
import eu.davidea.samples.flexibleadapter.fragments.FragmentEndlessScrolling;
import eu.davidea.samples.flexibleadapter.fragments.FragmentExpandableMultiLevel;
import eu.davidea.samples.flexibleadapter.fragments.FragmentExpandableSections;
Expand Down Expand Up @@ -340,6 +341,8 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
mFragment = FragmentEndlessScrolling.newInstance(2);
} else if (id == R.id.nav_instagram_headers) {
mFragment = FragmentInstagramHeaders.newInstance();
} else if (id == R.id.nav_db_headers_and_sections) {
mFragment = FragmentDataBindingHeadersSections.newInstance(2);
} else if (id == R.id.nav_headers_and_sections) {
mFragment = FragmentHeadersSections.newInstance(2);
fabBehavior.setEnabled(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package eu.davidea.samples.flexibleadapter.fragments;

import android.databinding.DataBindingUtil;
import android.databinding.ObservableArrayList;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

import eu.davidea.fastscroller.FastScroller;
import eu.davidea.flexibleadapter.databinding.BindingFlexibleAdapter;
import eu.davidea.flexibleadapter.SelectableAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollGridLayoutManager;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import eu.davidea.flexibleadapter.items.IHeader;
import eu.davidea.flexibleadapter.items.ISectionable;
import eu.davidea.flipview.FlipView;
import eu.davidea.samples.flexibleadapter.MainActivity;
import eu.davidea.samples.flexibleadapter.R;
import eu.davidea.samples.flexibleadapter.databinding.FragmentDatabindingRecyclerViewBinding;
import eu.davidea.samples.flexibleadapter.dialogs.OnParameterSelectedListener;
import eu.davidea.samples.flexibleadapter.models.ExpandableHeaderItem;
import eu.davidea.samples.flexibleadapter.models.HeaderItem;
import eu.davidea.samples.flexibleadapter.services.DatabaseConfiguration;
import eu.davidea.samples.flexibleadapter.services.DatabaseService;
import eu.davidea.utils.Utils;

/**
* A fragment representing a list of Items.
* Activities containing this fragment MUST implement the {@link OnFragmentInteractionListener}
* interface.
*/
public class FragmentDataBindingHeadersSections extends AbstractFragment
implements OnParameterSelectedListener {

public static final String TAG = FragmentDataBindingHeadersSections.class.getSimpleName();

private ObservableArrayList<AbstractFlexibleItem> items = new ObservableArrayList<>();
private FragmentDatabindingRecyclerViewBinding binding;

private int fabClickedTimes = 0;
/**
* Custom implementation of FlexibleAdapter
*/
private BindingFlexibleAdapter<AbstractFlexibleItem> mAdapter;


public static FragmentDataBindingHeadersSections newInstance(int columnCount) {
FragmentDataBindingHeadersSections fragment = new FragmentDataBindingHeadersSections();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}

/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public FragmentDataBindingHeadersSections() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_databinding_recycler_view, container, false);
binding.setItems(items);

return binding.getRoot();
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Settings for FlipView
FlipView.resetLayoutAnimationDelay(true, 1000L);

//Create New Database and Initialize RecyclerView
DatabaseService.getInstance().createHeadersSectionsDatabase(20, 5);
initializeRecyclerView(savedInstanceState);

//Restore FAB button and icon
initializeFab();

//Settings for FlipView
FlipView.stopLayoutAnimation();
}

@SuppressWarnings({"ConstantConditions", "NullableProblems"})
private void initializeRecyclerView(Bundle savedInstanceState) {
//Initialize Adapter and RecyclerView
//ExampleAdapter makes use of stableIds, I strongly suggest to implement 'item.hashCode()'
mAdapter = new BindingFlexibleAdapter<>(getActivity());
//Experimenting NEW features (v5.0.0)
mAdapter.setRemoveOrphanHeaders(false)
.setNotifyChangeOfUnfilteredItems(true)//We have highlighted text while filtering, so let's enable this feature to be consistent with the active filter
.setAnimationOnScrolling(DatabaseConfiguration.animateOnScrolling);
mRecyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(createNewLinearLayoutManager());
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(true); //Size of RV will not change
//NOTE: Use default item animator 'canReuseUpdatedViewHolder()' will return true if
// a Payload is provided. FlexibleAdapter is actually sending Payloads onItemChange.
mRecyclerView.setItemAnimator(new DefaultItemAnimator());

//Add FastScroll to the RecyclerView, after the Adapter has been attached the RecyclerView!!!
mAdapter.setFastScroller((FastScroller) getView().findViewById(R.id.fast_scroller),
Utils.getColorAccent(getActivity()), (MainActivity) getActivity());
mAdapter.setLongPressDragEnabled(true)
.setHandleDragEnabled(true)
.setSwipeEnabled(true)
.setUnlinkAllItemsOnRemoveHeaders(true)
//Show Headers at startUp, 1st call, correctly executed, no warning log message!
.setDisplayHeadersAtStartUp(true)
.enableStickyHeaders()
//Simulate developer 2nd call mistake, now it's safe, not executed, no warning log message!
.setDisplayHeadersAtStartUp(true)
//Simulate developer 3rd call mistake, still safe, not executed, warning log message displayed!
.showAllHeaders();

SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) getView().findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setEnabled(true);
mListener.onFragmentChange(swipeRefreshLayout, mRecyclerView, SelectableAdapter.MODE_IDLE);

//Add sample HeaderView items on the top (not belongs to the library)
}

@Override
public void performFabAction() {
if (fabClickedTimes == 0) {
items.addAll(DatabaseService.getInstance().getDatabaseList());
} else if (fabClickedTimes == 1) {
items.add(0, DatabaseService.newSimpleItem(fabClickedTimes * 111, null));
} else if (fabClickedTimes == 2) {
HeaderItem headerItem = DatabaseService.newHeader(1);
items.add(1, DatabaseService.newSimpleItem(fabClickedTimes * 111, headerItem));
} else {
items.add(1, DatabaseService.newSimpleItem(fabClickedTimes * 111, null));
}
++fabClickedTimes;
}

@Override
public void showNewLayoutInfo(MenuItem item) {
super.showNewLayoutInfo(item);
}

@Override
public void onParameterSelected(int itemType, int referencePosition, int childPosition) {
if (referencePosition < 0) return;
int scrollTo, id;
IHeader referenceHeader = getReferenceList().get(referencePosition);
Log.d(TAG, "Adding New Item: ItemType=" + itemType +
" referencePosition=" + referencePosition +
" childPosition=" + childPosition);
switch (itemType) {
case 1: //Expandable
id = mAdapter.getItemCountOfTypes(R.layout.recycler_expandable_item) + 1;
ISectionable sectionableExpandable = DatabaseService.newExpandableItem(id, referenceHeader);
mAdapter.addItemToSection(sectionableExpandable, referenceHeader, childPosition);
scrollTo = mAdapter.getGlobalPositionOf(referenceHeader);
break;
case 2: //Expandable Header
id = mAdapter.getItemCountOfTypes(R.layout.recycler_expandable_header_item) + 1;
ExpandableHeaderItem expandableHeader = DatabaseService.newExpandableSectionItem(id);
expandableHeader.setExpanded(false);
mAdapter.addSection(expandableHeader, referenceHeader);
scrollTo = mAdapter.getGlobalPositionOf(expandableHeader);
break;
case 3: //Header
id = mAdapter.getItemCountOfTypes(R.layout.recycler_header_item) + 1;
IHeader header = DatabaseService.newHeader(id);
mAdapter.addSection(header, referenceHeader);
scrollTo = mAdapter.getGlobalPositionOf(header);
break;
default: //case 0 = Simple Item
id = mAdapter.getItemCountOfTypes(R.layout.recycler_expandable_item) + 1;
ISectionable sectionable = DatabaseService.newSimpleItem(id, referenceHeader);
mAdapter.addItemToSection(sectionable, referenceHeader, childPosition);
scrollTo = mAdapter.getGlobalPositionOf(referenceHeader);
}

//With Sticky Headers enabled, this seems necessary to give
// time at the RV to be in correct state before scrolling
final int scrollToFinal = scrollTo;
mRecyclerView.post(new Runnable() {
@Override
public void run() {
mRecyclerView.smoothScrollToPosition(scrollToFinal);
}
});
}

@Override
public List<IHeader> getReferenceList() {
return mAdapter.getHeaderItems();
}

@Override
protected GridLayoutManager createNewGridLayoutManager() {
GridLayoutManager gridLayoutManager = new SmoothScrollGridLayoutManager(getActivity(), mColumnCount);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
//NOTE: If you use simple integer to identify the ViewType,
//here, you should use them and not Layout integers
switch (mAdapter.getItemViewType(position)) {
case R.layout.recycler_layout_item:
case R.layout.recycler_uls_item:
case R.layout.recycler_header_item:
case R.layout.recycler_expandable_header_item:
return mColumnCount;
default:
return 1;
}
}
});
return gridLayoutManager;
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
Log.v(TAG, "onCreateOptionsMenu called!");
inflater.inflate(R.menu.menu_sections, menu);
mListener.initSearchView(menu);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);

menu.findItem(R.id.action_auto_collapse).setVisible(false);
menu.findItem(R.id.action_expand_collapse_all).setVisible(false);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
<variable
name="items"
type="android.databinding.ObservableArrayList" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AppTheme.Base">

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:enabled="true"><!-- To restore to false when bug is fixed by Android team -->

<!-- This FrameLayout is needed ONLY IF sticky headers are used, in order to
display the refresh circle on the top of sticky_header_layout.
If you need to drag items, SwipeRefreshLayout must be disabled! -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items="@{items}"
tools:listitem="@layout/recycler_expandable_item" />

<!-- Needed by FlexibleAdapter for headers to be sticky.
This little layout is included in the library.
If you need to drag items, SwipeRefreshLayout must be disabled!-->
<include layout="@layout/sticky_header_layout" />

</FrameLayout>

</android.support.v4.widget.SwipeRefreshLayout>

<!-- Needed by FlexibleAdapter for headers to be sticky.
This little layout is included in the library.
Declared here, only in combination with SwipeRefreshLayout,
the refresh circle will appear below the sticky header!! -->
<!--<include layout="@layout/sticky_header_layout"/>-->

<!-- Custom EmptyView for RV when empty-->
<include layout="@layout/empty_view" />

<!-- Custom ProgressBar (at the moment not used) -->
<!--<include layout="@layout/progress_bar"/>-->

<!-- FastScroller Layout must be at the end of ViewHierarchy
in order to be displayed at the top of every views -->
<include layout="@layout/fast_scroller" />

</FrameLayout>
</layout>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
android:id="@+id/nav_headers_and_sections"
android:icon="@drawable/ic_sections_grey600_24dp"
android:title="@string/headers_sections"/>
<item
android:id="@+id/nav_db_headers_and_sections"
android:icon="@drawable/ic_sections_grey600_24dp"
android:title="@string/headers_db_sections"/>
<item
android:id="@+id/nav_expandable_sections"
android:icon="@drawable/ic_expandable_grey_600_24dp"
Expand Down
2 changes: 2 additions & 0 deletions flexible-adapter-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ items and higher, due to calculate the correct position for each item to shift.
<string name="headers_sections">Headers and Sections</string>
<string name="headers_sections_description">Clickable sticky headers for sections + Draggable items with Auto-Linkage + Filter</string>

<string name="headers_db_sections">Headers and Sections - databinding</string>

<string name="expandable_sections">Expandable Sections</string>
<string name="expandable_sections_description">Sections with [sticky] headers that can expand/collapse + Draggable items + Filter + Scroll Animations</string>

Expand Down
4 changes: 4 additions & 0 deletions flexible-adapter/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

dataBinding {
enabled = true
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public class FlexibleAdapter<T extends IFlexible>
/**
* The main container for ALL items.
*/
private List<T> mItems, mTempItems;
protected List<T> mItems, mTempItems;

/**
* HashSet, AsyncTask and DiffUtil objects, will increase performance in big list
Expand Down
Loading

0 comments on commit bb88602

Please sign in to comment.