6.7.0 - Alpha23 - 移除 "android.enableJetifier=true"; 模块化 Expandable Layout, Expandable RecyclerView, Recyclerview Flexible Divider

This commit is contained in:
SuperMonster003
2026-03-07 19:35:05 +08:00
parent 0cc28bdb6c
commit 528f3e59dc
47 changed files with 4673 additions and 75 deletions

View File

@@ -0,0 +1 @@
<manifest />

View File

@@ -0,0 +1,70 @@
package com.bignerdranch.expandablerecyclerview;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
/**
* ViewHolder for a child list item.
* <p>
* The user should extend this class and implement as they wish for their
* child list item.
*/
public class ChildViewHolder<C> extends RecyclerView.ViewHolder {
C mChild;
ExpandableRecyclerAdapter mExpandableAdapter;
/**
* Default constructor.
*
* @param itemView The {@link View} being hosted in this ViewHolder
*/
public ChildViewHolder(@NonNull View itemView) {
super(itemView);
}
/**
* @return the childListItem associated with this view holder
*/
@UiThread
public C getChild() {
return mChild;
}
/**
* Returns the adapter position of the Parent associated with this ChildViewHolder
*
* @return The adapter position of the Parent if it still exists in the adapter.
* RecyclerView.NO_POSITION if item has been removed from the adapter,
* RecyclerView.Adapter.notifyDataSetChanged() has been called after the last
* layout pass or the ViewHolder has already been recycled.
*/
@UiThread
public int getParentAdapterPosition() {
int flatPosition = getAdapterPosition();
if (mExpandableAdapter == null || flatPosition == RecyclerView.NO_POSITION) {
return RecyclerView.NO_POSITION;
}
return mExpandableAdapter.getNearestParentPosition(flatPosition);
}
/**
* Returns the adapter position of the Child associated with this ChildViewHolder
*
* @return The adapter position of the Child if it still exists in the adapter.
* RecyclerView.NO_POSITION if item has been removed from the adapter,
* RecyclerView.Adapter.notifyDataSetChanged() has been called after the last
* layout pass or the ViewHolder has already been recycled.
*/
@UiThread
public int getChildAdapterPosition() {
int flatPosition = getAdapterPosition();
if (mExpandableAdapter == null || flatPosition == RecyclerView.NO_POSITION) {
return RecyclerView.NO_POSITION;
}
return mExpandableAdapter.getChildPosition(flatPosition);
}
}

View File

@@ -0,0 +1,201 @@
package com.bignerdranch.expandablerecyclerview;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import com.bignerdranch.expandablerecyclerview.model.Parent;
/**
* ViewHolder for a {@link Parent}
* Keeps track of expanded state and holds callbacks which can be used to
* trigger expansion-based events.
*
* @author Ryan Brooks
* @version 1.0
* @since 5/27/2015
*/
public class ParentViewHolder<P extends Parent<C>, C> extends RecyclerView.ViewHolder implements View.OnClickListener {
@Nullable
private ParentViewHolderExpandCollapseListener mParentViewHolderExpandCollapseListener;
private boolean mExpanded;
P mParent;
ExpandableRecyclerAdapter mExpandableAdapter;
/**
* Empowers {@link com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter}
* implementations to be notified of expand/collapse state change events.
*/
interface ParentViewHolderExpandCollapseListener {
/**
* Called when a parent is expanded.
*
* @param flatParentPosition The index of the parent in the list being expanded
*/
@UiThread
void onParentExpanded(int flatParentPosition);
/**
* Called when a parent is collapsed.
*
* @param flatParentPosition The index of the parent in the list being collapsed
*/
@UiThread
void onParentCollapsed(int flatParentPosition);
}
/**
* Default constructor.
*
* @param itemView The {@link View} being hosted in this ViewHolder
*/
@UiThread
public ParentViewHolder(@NonNull View itemView) {
super(itemView);
mExpanded = false;
}
/**
* @return the Parent associated with this ViewHolder
*/
@UiThread
public P getParent() {
return mParent;
}
/**
* Returns the adapter position of the Parent associated with this ParentViewHolder
*
* @return The adapter position of the Parent if it still exists in the adapter.
* RecyclerView.NO_POSITION if item has been removed from the adapter,
* RecyclerView.Adapter.notifyDataSetChanged() has been called after the last
* layout pass or the ViewHolder has already been recycled.
*/
@UiThread
public int getParentAdapterPosition() {
int flatPosition = getAdapterPosition();
if (flatPosition == RecyclerView.NO_POSITION) {
return flatPosition;
}
return mExpandableAdapter.getNearestParentPosition(flatPosition);
}
/**
* Sets a {@link android.view.View.OnClickListener} on the entire parent
* view to trigger expansion.
*/
@UiThread
public void setMainItemClickToExpand() {
itemView.setOnClickListener(this);
}
/**
* Returns expanded state for the {@link Parent}
* corresponding to this {@link ParentViewHolder}.
*
* @return true if expanded, false if not
*/
@UiThread
public boolean isExpanded() {
return mExpanded;
}
/**
* Setter method for expanded state, used for initialization of expanded state.
* changes to the state are given in {@link #onExpansionToggled(boolean)}
*
* @param expanded true if expanded, false if not
*/
@UiThread
public void setExpanded(boolean expanded) {
mExpanded = expanded;
}
/**
* Callback triggered when expansion state is changed, but not during
* initialization.
* <p>
* Useful for implementing animations on expansion.
*
* @param expanded true if view is expanded before expansion is toggled,
* false if not
*/
@UiThread
public void onExpansionToggled(boolean expanded) {
}
/**
* Setter for the {@link ParentViewHolderExpandCollapseListener} implemented in
* {@link com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter}.
*
* @param parentViewHolderExpandCollapseListener The {@link ParentViewHolderExpandCollapseListener} to set on the {@link ParentViewHolder}
*/
@UiThread
void setParentViewHolderExpandCollapseListener(ParentViewHolderExpandCollapseListener parentViewHolderExpandCollapseListener) {
mParentViewHolderExpandCollapseListener = parentViewHolderExpandCollapseListener;
}
/**
* {@link android.view.View.OnClickListener} to listen for click events on
* the entire parent {@link View}.
* <p>
* Only registered if {@link #shouldItemViewClickToggleExpansion()} is true.
*
* @param v The {@link View} that is the trigger for expansion
*/
@Override
@UiThread
public void onClick(View v) {
if (mExpanded) {
collapseView();
} else {
expandView();
}
}
/**
* Used to determine whether a click in the entire parent {@link View}
* should trigger row expansion.
* <p>
* If you return false, you can call {@link #expandView()} to trigger an
* expansion in response to a another event or {@link #collapseView()} to
* trigger a collapse.
*
* @return true to set an {@link android.view.View.OnClickListener} on the item view
*/
@UiThread
public boolean shouldItemViewClickToggleExpansion() {
return true;
}
/**
* Triggers expansion of the parent.
*/
@UiThread
protected void expandView() {
setExpanded(true);
onExpansionToggled(false);
if (mParentViewHolderExpandCollapseListener != null) {
mParentViewHolderExpandCollapseListener.onParentExpanded(getAdapterPosition());
}
}
/**
* Triggers collapse of the parent.
*/
@UiThread
protected void collapseView() {
setExpanded(false);
onExpansionToggled(true);
if (mParentViewHolderExpandCollapseListener != null) {
mParentViewHolderExpandCollapseListener.onParentCollapsed(getAdapterPosition());
}
}
}

View File

@@ -0,0 +1,125 @@
package com.bignerdranch.expandablerecyclerview.model;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
/**
* Wrapper used to link metadata with a list item.
*
* @param <P> Parent list item
* @param <C> Child list item
*/
public class ExpandableWrapper<P extends Parent<C>, C> {
private P mParent;
private C mChild;
private boolean mWrappedParent;
private boolean mExpanded;
private List<ExpandableWrapper<P, C>> mWrappedChildList;
/**
* Constructor to wrap a parent object of type {@link P}.
*
* @param parent The parent object to wrap
*/
public ExpandableWrapper(@NonNull P parent) {
mParent = parent;
mWrappedParent = true;
mExpanded = false;
mWrappedChildList = generateChildItemList(parent);
}
/**
* Constructor to wrap a child object of type {@link C}
*
* @param child The child object to wrap
*/
public ExpandableWrapper(@NonNull C child) {
mChild = child;
mWrappedParent = false;
mExpanded = false;
}
public P getParent() {
return mParent;
}
public void setParent(@NonNull P parent) {
mParent = parent;
mWrappedChildList = generateChildItemList(parent);
}
public C getChild() {
return mChild;
}
public boolean isExpanded() {
return mExpanded;
}
public void setExpanded(boolean expanded) {
mExpanded = expanded;
}
public boolean isParent() {
return mWrappedParent;
}
/**
* @return The initial expanded state of a parent
* @throws IllegalStateException If a parent isn't being wrapped
*/
public boolean isParentInitiallyExpanded() {
if (!mWrappedParent) {
throw new IllegalStateException("Parent not wrapped");
}
return mParent.isInitiallyExpanded();
}
/**
* @return The list of children of a parent
* @throws IllegalStateException If a parent isn't being wrapped
*/
public List<ExpandableWrapper<P, C>> getWrappedChildList() {
if (!mWrappedParent) {
throw new IllegalStateException("Parent not wrapped");
}
return mWrappedChildList;
}
private List<ExpandableWrapper<P, C>> generateChildItemList(P parentListItem) {
List<ExpandableWrapper<P, C>> childItemList = new ArrayList<>();
for (C child : parentListItem.getChildList()) {
childItemList.add(new ExpandableWrapper<P, C>(child));
}
return childItemList;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ExpandableWrapper<?, ?> that = (ExpandableWrapper<?, ?>) o;
if (mParent != null ? !mParent.equals(that.mParent) : that.mParent != null)
return false;
return mChild != null ? mChild.equals(that.mChild) : that.mChild == null;
}
@Override
public int hashCode() {
int result = mParent != null ? mParent.hashCode() : 0;
result = 31 * result + (mChild != null ? mChild.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,26 @@
package com.bignerdranch.expandablerecyclerview.model;
import java.util.List;
/**
* Interface for implementing required methods in a parent.
*/
public interface Parent<C> {
/**
* Getter for the list of this parent's child items.
* <p>
* If list is empty, the parent has no children.
*
* @return A {@link List} of the children of this {@link Parent}
*/
List<C> getChildList();
/**
* Getter used to determine if this {@link Parent}'s
* {@link android.view.View} should show up initially as expanded.
*
* @return true if expanded, false if not
*/
boolean isInitiallyExpanded();
}

View File

@@ -0,0 +1,32 @@
package com.bignerdranch.expandablerecyclerview.model;
import java.util.List;
/**
* Simple implementation of the ParentListItem interface,
* by default all items are not initially expanded.
*
* @param <C> Type of the Child Items held by the Parent.
*/
public class SimpleParent<C> implements Parent<C> {
private List<C> mChildList;
protected SimpleParent(List<C> childItemList) {
mChildList = childItemList;
}
@Override
public boolean isInitiallyExpanded() {
return false;
}
@Override
public List<C> getChildList() {
return mChildList;
}
public void setChildList(List<C> childList) {
mChildList = childList;
}
}