davideas / flexibleadapter Goto Github PK
View Code? Open in Web Editor NEWFast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
License: Apache License 2.0
Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
License: Apache License 2.0
Adapter should not call notifyDataSetChanged()
anymore while filtering. Instead items should be added and/or removed from the adapter list depending by the searchText and the final change should be animated.
Evaluate if the external library FastScroller should be included in this library.
Using RecyclerView.ItemDecoration creates a divider after every row and header. Is there a way to have the divider only show at the end of a section? Or should I make the divider a row?
Section header 1
section item 1
section item 2
divider
Section header 2
section item 1 (no divider here)
Simplify ALL constructors. The adapter should recognize which type of listener an Activity or a Fragment is implementing the events triggered by the User or by the adapter.
Give the possibility to Developers to decide which listeners to implement one by one.
Maybe pass 1 single parameter to represent all listeners that object belongs to, or use prefix-with methodology to set the listeners at startup.
When clicking sub item or remove sub item, will be appear following error:
FATAL EXCEPTION: main Process: eu.davidea.examples.flexibleadapter, PID: 28911 java.lang.ClassCastException: eu.davidea.examples.models.SubItem cannot be cast to eu.davidea.examples.models.SimpleItem at eu.davidea.examples.flexibleadapter.MainActivity.extractTitleFrom(MainActivity.java:739) at eu.davidea.examples.flexibleadapter.MainActivity.onItemSwipe(MainActivity.java:492) at eu.davidea.flexibleadapter.FlexibleAdapter.onItemSwiped(FlexibleAdapter.java:2331) at eu.davidea.flexibleadapter.helpers.ItemTouchHelperCallback.onSwiped(ItemTouchHelperCallback.java:159) at android.support.v7.widget.helper.ItemTouchHelper$4.run(ItemTouchHelper.java:669) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
This is an open discussion to keep track of all the work in progress of the next coming pre-releases in order to arrive to a stable and final version.
In this major release (5.0.0) there will be lot of new features, while trying to simplify the configuration to speed up the development. A full list of the features and improvements, with their status, can be found here Milestone 5.0.0. However we can identify them in:
In order to do them, I am completely changing and refactoring the code, names and how the Adapter will be extended to override its methods.
allprojects {
repositories {
// For [pre-]releases
jcenter()
// For SNAPSHOT
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
}
// Don't cache SNAPSHOT (changing) dependencies.
configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
dependencies {
// JCenter for PreReleases
compile 'eu.davidea:flexible-adapter:5.0.0-rc3'
compile 'eu.davidea:flexible-adapter-livedata:1.0.0-b1'
compile 'eu.davidea:flexible-adapter-databinding:1.0.0-b2'
// Maven for SNAPSHOT
compile ('eu.davidea:flexible-adapter:5.0.0-SNAPSHOT') {
changing = true;
}
}
As soon as a new SNAPSHOT is ready and usable I will communicate it here.
Version | Reason for publishing | Description |
---|---|---|
5.0.0-SNAPSHOT |
continuous development | A library with many changes that might be unstable, nice to have to early benefit of the latest bug fix, improvements and new features as soon as they are implemented. It will replace the existent library (with that name) in the maven snapshot repository. So you don't have to manually change the version in the dependencies, but expect to upgrade your code in order to continue to compile your project. Gradle has internal caching system with default time of 24h. Passed this time it will fetch the new Snapshot file from repository, if a new exists. |
5.0.0-bX |
beta pre-release | Usable beta library with some new features to test. |
5.0.0-rcX |
release candidates | Nearly completed library with good stability and improved features |
5.0.0 |
release | The stable library with all new features confirmed and tested. |
It should also there be a way to identify the position where to add the items and the section.
I have copied the two classes and extended the Flexible adapter class in my adapter. My problem now is when i enter text in search view the text gets highlighted but it does not get filtered. I know im going wrong somewhere but i cant figure out where. Below is the code of my adapter class.
public class FertilizerListAdapter extends FlexibleAdapter<FertilizerListAdapter.FertilizerViewHolder, Fertilizer> {
List<Fertilizer> items;
Context context;
LayoutInflater mInflater;
public FertilizerListAdapter(Context context, List<Fertilizer> items, String param) {
this.items = items;
this.context = context;
mInflater = LayoutInflater.from(context);
updateDataSet(param);
}
@Override
public void updateDataSet(String param) {
}
@Override
public FertilizerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View productItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.fertilizer_list_item, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
productItem.setLayerType(View.LAYER_TYPE_HARDWARE, null);
FertilizerViewHolder productItemVH = new FertilizerViewHolder(productItem);
return productItemVH;
}
@Override
public void onBindViewHolder(FertilizerViewHolder holder, final int position) {
Fertilizer item = items.get(position);
Typeface custom_font = Typeface.createFromAsset(context.getAssets(), "fonts/RobotoSlab-Light.ttf");
holder.mProductName.setText(items.get(position).sProductName);
holder.mProductPrice.setText(items.get(position).sProductPrice);
holder.mShortDesc.setText(items.get(position).sShortDesc);
ColorGenerator generator = ColorGenerator.DEFAULT;
int color = generator.getRandomColor();
TextDrawable drawable = TextDrawable.builder()
.beginConfig()
.textColor(Color.WHITE)
.toUpperCase()
.useFont(custom_font)
.fontSize(30)
.endConfig()
.buildRound(String.valueOf(items.get(position).sProductName.charAt(0)), color);
Picasso.with(context)
.load(items.get(position).sProductImage)
.placeholder(drawable)
.error(drawable)
.into(holder.mProductImage);
holder.itemView.setTag(item);
if (hasSearchText()) {
setHighlightText(holder.mProductName, item.sProductName, mSearchText);
setHighlightText(holder.mShortDesc, item.sShortDesc, mSearchText);
} else {
holder.mProductName.setText(item.sProductName);
holder.mShortDesc.setText(item.sShortDesc);
}
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(context, FertilizerView.class);
i.putExtra("ProductApplication", (items.get(position).sApplication));
i.putExtra("ProductAvailability", (items.get(position).sAvailability));
i.putExtra("ProductBenefits", (items.get(position).sBenefits));
i.putExtra("ProductChemicalContent", (items.get(position).sChemicalContent));
i.putExtra("ProductCrops", (items.get(position).sCrops));
i.putExtra("ProductBacgroundImage", (items.get(position).sProductBackground));
i.putExtra("ProductImage", (items.get(position).sProductImage));
i.putExtra("ProductName", (items.get(position).sProductName));
i.putExtra("ProductPrice", (items.get(position).sProductPrice));
i.putExtra("ProductDescription", (items.get(position).sShortDesc));
context.startActivity(i);
}
});
}
@Override
public int getItemCount() {
return items.size();
}
public static class FertilizerViewHolder extends RecyclerView.ViewHolder {
ImageView mProductImage;
TextView mProductName;
TextView mShortDesc, mProductPrice;
View mView;
public FertilizerViewHolder(View itemView) {
super(itemView);
mView = itemView;
mProductImage = (ImageView) itemView.findViewById(R.id.product_image);
mProductName = (TextView) itemView.findViewById(R.id.product_name);
mProductPrice = (TextView) itemView.findViewById(R.id.price);
mShortDesc = (TextView) itemView.findViewById(R.id.shortDesc);
}
}
private void setHighlightText(TextView textView, String text, String searchText) {
Spannable spanText = Spannable.Factory.getInstance().newSpannable(text);
int i = text.toLowerCase(Locale.getDefault()).indexOf(searchText);
if (i != -1) {
spanText.setSpan(new ForegroundColorSpan(Utils.getColorAccent(context)), i,
i + searchText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanText.setSpan(new StyleSpan(Typeface.BOLD), i,
i + searchText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spanText, TextView.BufferType.SPANNABLE);
} else {
textView.setText(text, TextView.BufferType.NORMAL);
}
}
@Override
protected boolean filterObject(Fertilizer fertilizer, String constraint) {
String valueText = fertilizer.sProductName;
//Filter on Title
if (valueText != null && valueText.toLowerCase().contains(constraint)) {
return true;
}
//Filter on Subtitle
valueText = fertilizer.sShortDesc;
return valueText != null && valueText.toLowerCase().contains(constraint);
}
}
I am trying to think on how to integrate your lib (5.0.0).
In my use case getting a List to use with the adapter is really an options.
I hold a list of sections which each holds a list of items and a possible header.
I wondering if there might be a way to use the FlexibleAdapter by subclassing it to get the item from my sections.
It seems doable but i might have issues making it work with all the features:
getItemCount = sectionsCount*ItemCount
I think this might require some refactoring to do things like this.
I also think it would be doable without changing anything performance wise.
For example:
public List<Integer> getExpandedPositions() {
List<Integer> expandedPositions = new ArrayList<Integer>();
for (int i = 0; i < mItems.size() - 1; i++) {
T item = mItems.get(i);
if (isExpanded(item))
expandedPositions.add(i);
}
return expandedPositions;
}
would become
public List<Integer> getExpandedPositions() {
List<Integer> expandedPositions = new ArrayList<Integer>();
for (int i = 0; i < getItemCount() - 1; i++) {
T item = getItem(i);
if (isExpanded(item))
expandedPositions.add(i);
}
return expandedPositions;
}
What do you think?
FYI i am trying to integrate your lib in my fork of the Titanium Mobile framework
Is it possible to configure the FlexibleAdapter to have sticky headers inside a section, not above it?
What I mean is to have items look like so:
=== section name ===
== sticky header 1 ==
= item 1.1 =
= item 1.2 =
== sticky header 2 ==
= item 2.1 =
= item 2.2 =
The point is that the section names do not stick to the top of the screen but the sticky headers which are inside the sections do.
Should launch the SnackBar automatically and interact with the Adapter.
Is there a way to sort items without inserting them at the correct position?
Thanks!
Hi,
I am testing 5.0.0-b5 and found a bug in StickyHeaderHelper#translateHeader() on row 151. You are expecting next potential header on position 1. But if for example I use 3 columns in my grid, you will always get current header until all items in section are off the screen.
So you won't get this nice pushing of one header off the screen by another header.
I was able to reproduce this in your demo app, when I changed span count of grid to 3 and span size of all items except header to 1.
Anyway, thanks for great work.
Could you provide an example with checkbox selection?
For some users a long click to select is sometimes "hard to understand" or in other words "they don't know what to do". ;-)
Sometimes you want track onDeleteConfirmed
but not necessarily onLoadComplete
and vice and versa
Expandable and Not expandable items should have selection coherence, meaning that is not possible to select both View types.
Filtering should automatically expand the expandable if searchText exists in his subItems.
Thx
I'm trying to follow what you have in the example app, but I can't get sticky headers to work with my app. The header view shows up but isn't sticky.
gradle
compile 'eu.davidea:flexible-adapter:5.0.0-SNAPSHOT'
maven {url = "https://oss.sonatype.org/content/repositories/snapshots/" } //For Snapshots
Adapter
public class RecentContactsAdapter extends FlexibleAdapter<AbstractFlexibleItem> {
private final Context context;
private List<GroupedRecentCall> groupedRecentCallList;
private Map<String, String> callLogToPhoneMap;
private List<AbstractFlexibleItem> recentCallsWithHeaders;
private Map<Integer, LocalDataForRecentCall> localMap;
public RecentContactsAdapter(Context context, List<AbstractFlexibleItem> recentCallsWithHeaders) {
super(recentCallsWithHeaders);
this.recentCallsWithHeaders = recentCallsWithHeaders;
this.context = context;
localMap = new HashMap<Integer, LocalDataForRecentCall>();
callLogToPhoneMap = new HashMap<String, String>();
}
}
Header
public class RecentContactHeaderItem extends AbstractHeaderItem<RecentContactHeaderItem.ViewHolder> {
private String title;
public RecentContactHeaderItem(String title) {
super();
this.title = title;}
@Override
public boolean equals(Object o) {
if (o instanceof String) {
String oo = (String) o;
return oo.equals(this.title);
}
return false;
}
@Override
public int getLayoutRes() {
return R.layout.recent_contacts_header;
}
@Override
public ViewHolder createViewHolder(FlexibleAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
return new ViewHolder(inflater.inflate(getLayoutRes(), parent, false), adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, List payloads) {
holder.titleView.setText(title);
}
public static class ViewHolder extends FlexibleViewHolder {
public OpenSansTextView titleView;
public ViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
this.titleView = (OpenSansTextView) view.findViewById(R.id.title);
}
}
}
Section item
public class RecentContactItem extends AbstractSectionableItem<RecentContactItem.ViewHolder, RecentContactHeaderItem> {
private final String TAG = "RecentContactItem";
private Context context;
private SimpleDateFormat timeFormatter;
private SimpleDateFormat dateFormatter;
private GroupedRecentCall groupedRecentCall;
private Map<String, String> callLogToPhoneMap;
private Map<Integer, RecentContactsAdapter.LocalDataForRecentCall> localMap;
private RoundedTransformation roundedTransformer;
private Bitmap defaultAvatar;
public RecentContactItem(Context context, RecentContactHeaderItem header, SimpleDateFormat timeFormatter,
SimpleDateFormat dateFormatter, GroupedRecentCall groupedRecentCall,
HashMap<Integer, RecentContactsAdapter.LocalDataForRecentCall> localMap,
HashMap<String, String> callLogToPhoneMap) {
this.context = context;
setHeader(header);
}
@Override
public boolean equals(Object o) {
if (o instanceof CallLogObject) {
GroupedRecentCall inItem = (GroupedRecentCall) o;
return this.groupedRecentCall.getCallLogObject().getCallDate().equals(inItem.getCallLogObject().getCallDate());
}
return false;
}
@Override
public int getLayoutRes() {
return R.layout.recent_contacts_item;
}
@Override
public ViewHolder createViewHolder(FlexibleAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
return new ViewHolder(inflater.inflate(getLayoutRes(), parent, false), adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, List payloads) {
// binding
}
public static final class ViewHolder extends FlexibleViewHolder {
public ImageView avatarView;
public OpenSansTextView moreCountView;
public OpenSansTextView text1View;
public OpenSansTextView text2View;
public ImageView moreView;
public ViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
avatarView = (ImageView) view.findViewById(R.id.avatar);
moreCountView = (OpenSansTextView) view.findViewById(R.id.moreCount);
text1View = (OpenSansTextView) view.findViewById(R.id.text1);
text2View = (OpenSansTextView) view.findViewById(R.id.text2);
moreView = (ImageView) view.findViewById(R.id.more);
}
}
}
Fragment (the rest of the view is instantiated in onCreateView
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FlexibleAdapter.enableLogs(true);
Context context = getContext();
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED) {
CallLogUtils.getAllCallsFromLog(context, null);// use null so its not a recursive call to this function
}
SimpleDateFormat timeFormatter = new SimpleDateFormat("h:mm a");
SimpleDateFormat dateFormatter = new SimpleDateFormat("MMM d");
HashMap<Integer, RecentContactsAdapter.LocalDataForRecentCall> localMap = new HashMap<>();
HashMap<String, String> callLogToPhoneMap = new HashMap<>();
List<GroupedRecentCall> groupedRecentCallList = ModelManager.getCallsGrouped(context);
List<AbstractFlexibleItem> recentCallsWithHeaders = new ArrayList<>();
RecentContactHeaderItem communcationStates = new RecentContactHeaderItem("Communication Stats");
RecentContactHeaderItem frequentCallers = new RecentContactHeaderItem("Frequent callers");
RecentContactHeaderItem recentActivity = new RecentContactHeaderItem("Recent activity");
for (int i = 0; i < groupedRecentCallList.size(); i++) {
GroupedRecentCall groupedRecentCall = groupedRecentCallList.get(i);
RecentContactItem item = new RecentContactItem(context, (i < groupedRecentCallList.size() / 2) ? frequentCallers : recentActivity, timeFormatter,
dateFormatter, groupedRecentCall, localMap, callLogToPhoneMap);
item.setHeader((i < groupedRecentCallList.size() / 2) ? frequentCallers : recentActivity);
recentCallsWithHeaders.add(item);
}
adapter = new RecentContactsAdapter(context, recentCallsWithHeaders);
recyclerView = (RecyclerView) getView().findViewById(R.id.list);
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
// load adapter here for < 4.1
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
// listView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
return true;
}
});
adapter.setDisplayHeadersAtStartUp(true);
adapter.enableStickyHeaders();
}
Fragment layout
<?xml version="1.0" encoding="utf-8"?>
<!--- used by recent contacts and address book -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:background="@color/white"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
<!-- needed for FlexibleAdapter sticky headers -->
<include layout="@layout/sticky_header_layout"/>
<include
android:id="@android:id/empty"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="@layout/empty_layout" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:visibility="gone"/>
<spokeo.com.spokeomobile.views.OpenSansTextView
android:id="@+id/progressText"
android:layout_below="@id/progressBar"
android:layout_marginTop="10dp"
android:gravity="center"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="Loading Connections"
android:textColor="@color/dark_gray"
android:visibility="gone"/>
</RelativeLayout>
A working example would be greatly appreciated? The ExampleAdapter is not really clear since you renamed ManageLabelsAdapter to it, along with a plethora of "missing" classes associated with it. Thank you for the library.
ItemBackground for view activation should be configurable and loaded dynamically, no XML.
To include in the library.
The implementation of the empty view remains duty of the developer in the Activity, but the Adapter just triggers the notification for the empty View.
Can you upload it to JCenter ?
Once a header is drawn over others elements, it should be clickable.
This is not a bug due to the different representation/implementation of the view in the RecyclerView.
Optionally activate a custom animation to the items when RecyclerView is firstly loaded and shown.
I will add this feature on next major release 5.0.0
The implementation should extend android.support.v7.widget.helper.ItemTouchHelper.Callback
.
Item Animation should not overlap with Adapter Animation.
Modify ItemAnimators to be intercepted by AnimatorVH, that can return false to stop animator.
Add custom animation on children ItemViews.
updateDataSetAsync() will be deprecated in 4.2.0 and removed from next major release.
The inner class FilterAsyncTask will not be used anymore to load data at startup: there's no advantage to use an Asynchronous loading, usually the list is already loaded Asynchronously somewhere else.
Add a Wiki page with all functionalities and quick description of what the library can do.
Add and adjust Wiki pages with tutorials for v5.0.0 to show the main functionalities.
Add a side menu for different versions.
* = Initiated / Not completed
if the example app would be based on a Fragment instead of an Activity it would be much easier to adapt it for simple projects.
How I initialize my adapter:
mAdapter = new MyNewsFlexibleAdapter(new ArrayList<AbstractFlexibleItem>());
mAdapter.setDisplayHeadersAtStartUp(true);//Show Headers at startUp!`
// And after I get my recycler view
mRecycler.setAdapter(mAdapter);
mAdapter.enableStickyHeaders(3);
I have this method in my adapter
public void addNews(List<News> newsList) {
// create header
int categoryId = newsList.get(0).categoryId;
MyNewsHeaderItem header = new MyNewsHeaderItem(categoryId);
// add items for header
for (News news : newsList) {
MyNewsSmallItem newsSmallItem = new MyNewsSmallItem(news, header);
addItem(getItemCount(), newsSmallItem);
}
showAllHeaders();
}
And this method is called multiple times, if I call this method for 2 times I would like to have something like this
And instead the first header is added twice!
If this method gets called 3 times, the first header would be added 3 times, the second one twice...
How can I fix this ? If I don't call showAllHeaders() the headers won't show up, and this method can be called any time..
EDIT :
I found a solution
hideAllHeaders();
showAllHeaders();
EDIT 2: this will cause a NPE at FlexibleAdapter 827
And another question, how can I completely clear the list? Without creating a new adapter of course..
While scrolling, an header or a section can be "sticky" to the top (or to the bottom?).
It should pushed away when scrolling in favor of a new headers.
Hi @davideas
Thanks for great lib.
I am using FlexibleAdapter to display sticky headers in RecyclerView. It is working fine for the first time loading list data using adapter, however, I am getting below crash issue while pushing fresh list items in adapter for the second time onwards.
Below is my code.
Code in onCreateView() method:
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
//mRecyclerView.setHasFixedSize(true); //Size of RV will not change
mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
//NOTE: This allows to receive Payload objects on notifyItemChanged called by the Adapter!!!
return true;
}
});
mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), R.drawable.divider));
code in refreshData() method:(which will be called everytime when user want to refresh data)
ArrayList<AbstractFlexibleItem> items = new ArrayList<AbstractFlexibleItem>();
.....
......
add AbstractFlexibleItem objects(headers and simple items) to items.
....
.......
mAdapter = new FeedAdapter(items, FeedFragment.this);
mAdapter.setRemoveOrphanHeaders(false);
mRecyclerView.setAdapter(mAdapter);
mAdapter.showAllHeaders();
mAdapter.enableStickyHeaders(3);
Note: FeedAdapter extends FlexibleAdapter
I am not observing the crash issue while commenting showAllHeaders() method call, however, no headers are showing due to this.
Can you please help me to get out of this issue?
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.code, PID: 23833
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 15(offset:21).state:21
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4405)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3071)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1627)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
at android.view.View.layout(View.java:15140)
at android.view.ViewGroup.layout(ViewGroup.java:4866)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2342)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2048)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1211)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6282)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:788)
at android.view.Choreographer.doCallbacks(Choreographer.java:591)
at android.view.Choreographer.doFrame(Choreographer.java:560)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:774)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
E/NativeCrypto: ssl=0x60990e98 cert_verify_callback x509_store_ctx=0x61259940 arg=0x0
E/NativeCrypto: ssl=0x60990e98 cert_verify_callback calling verifyCertificateChain authMethod=ECDHE_RSA
Should launch ActionMode and handle the listeners.
I was think about add a new utility class FlexibleViewHolder
it will help developers it alwasy need to implements the View.OnClickListener
and View.OnLongClickListener
public abstract class FlexibleViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener {
private final FlexibleAdapter adapter;
private final OnListItemClickListener onListItemClickListener;
public FlexibleViewHolder(View itemView,FlexibleAdapter adapter,
OnListItemClickListener onListItemClickListener) {
super(itemView);
this.adapter = adapter;
this.onListItemClickListener = onListItemClickListener;
this.itemView.setOnClickListener(this);
this.itemView.setOnLongClickListener(this);
}
@Override
public void onClick(View view) {
if (onListItemClickListener.onListItemClick(getAdapterPosition())) {
toggleActivation();
}
}
@Override
public boolean onLongClick(View view) {
onListItemClickListener.onListItemLongClick(getAdapterPosition());
toggleActivation();
return true;
}
protected void toggleActivation() {
itemView.setActivated(adapter.isSelected(getAdapterPosition()));
}
public interface OnListItemClickListener {
boolean onListItemClick(int position);
void onListItemLongClick(int position);
}
}
So you can easily override the toggleActivation
to add whatever you want and implements the OnListItemClickListener
on your Activiy
/Fragment
New FastScroller:
Other sources:
When item is touched, the view can change elevation and when it is released previous elevation is restored.
TODO
isEnabled = no simple nor single nor multi selection, no touchable.
isSelectable = no single nor multi selection, cannot be deleted by user, probably not touchable, ok to delete programmaticaly.
isSwipeable = just cannot be swiped
isDraggable = just cannot be dragged
isHidden = just cannot be visible
DONE
isExpanded
isHidden (for searchView)
Deleting and restore should also continue to work.
Rather than just passing a String constraint to the filter method, it would be nice if you could pass an interface along the lines of:
boolean isAllowed(T item)
The item would be passed to the isAllowed
method and allow you to filter items with different conditions.
I am happy to work on a PR for this. Just wanted to check if this was something you were interested in supporting.
Should be available at the top and at the bottom.
Check around what is already implemented.
Special items can be identified as sections (such expandables or headers), they can be hidden or shown programmatically, adapter will reflect the change.
The project need to have some unit tests to help us to refactor/introduce new features.
It is not so clear only looking for the API or reading the wiki, you need populate the list of items in FlexibleAdapter
using the method filterItems
. If you don't do that you will get a RuntimeException.
IMHO populate the list of items should be a construction responsabilite. Wdyt?.
Add configuration Dialog or load new Activity to show all the power the library has.
Separate example with new Activities/Fragments, Sections with sticky headers, Expandable items, Expandable headers (to evaluate), Linear/Grid animations, Helpers use cases.
My header model
` */
public class MyNewsBigItem extends AbstractFlexibleItem<MyNewsBigItem.ViewHolder> implements ISectionable<MyNewsBigItem.ViewHolder, MyNewsHeaderItem> {
private MyNewsHeaderItem mHeader;
@Override
public MyNewsHeaderItem getHeader() {
return mHeader;
}
@Override
public MyNewsHeaderItem setHeader(MyNewsHeaderItem header) {
mHeader = header;
return header;
}
`
And my adapter looks like this:
`public class MyNewsFlexibleAdapter extends FlexibleAdapter {
public MyNewsFlexibleAdapter(List<AbstractFlexibleItem> items) {
super(items);
enableStickyHeaders(3);
MyNewsHeaderItem h1 = createHeaderItem();
MyNewsHeaderItem h2 = createHeaderItem();
MyNewsHeaderItem h3 = createHeaderItem();
MyNewsHeaderItem h4 = createHeaderItem();
addItem(getItemCount(), h1);
addItem(getItemCount(), createbigItem(h1));
addItem(getItemCount(), createbigItem(h1));
addItem(getItemCount(), createbigItem(h1));
addItem(getItemCount(), createbigItem(h1));
addItem(getItemCount(), h2);
addItem(getItemCount(), createbigItem(h2));
addItem(getItemCount(), createbigItem(h2));
addItem(getItemCount(), createbigItem(h2));
addItem(getItemCount(), createbigItem(h2));
addItem(getItemCount(), createbigItem(h2));
addItem(getItemCount(), h3);
addItem(getItemCount(), createbigItem(h3));
addItem(getItemCount(), h4);
addItem(getItemCount(), createbigItem(h4));
addItem(getItemCount(), createbigItem(h4));
addItem(getItemCount(), createbigItem(h4));
addItem(getItemCount(), createbigItem(h4));
}
`
Everything is shown correctly on the app, but the headers are not sticky. Where I'm wrong?
I've also tried to call the enableStickyHeaders after I set the adapter to the recyclerview. no success.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.