Initial commit

This commit is contained in:
吉浩茹
2026-01-21 21:16:14 +08:00
commit e6f7e7f26e
607 changed files with 57222 additions and 0 deletions

View File

@ -0,0 +1,13 @@
package com.superrecycleview.superlibrary;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.superrecycleview.superlibrary">
<application android:allowBackup="true" android:label="@string/app_name"
android:supportsRtl="true">
</application>
</manifest>

View File

@ -0,0 +1,10 @@
package com.superrecycleview.superlibrary.adapter;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public enum AnimationType {
ALPHA, SCALE, SLIDE_FROM_BOTTOM, SLIDE_FROM_LEFT, SLIDE_FROM_RIGHT
}

View File

@ -0,0 +1,89 @@
package com.superrecycleview.superlibrary.adapter;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.view.animation.Interpolator;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public class AnimationUtil {
private AnimationType mAnimationType = AnimationType.ALPHA;
private Animator mCustomAnimator;
private View mTargetView;
private Interpolator mInterpolator;
private int mDuration = 300;
public AnimationUtil() {
}
public AnimationUtil setAnimationType(AnimationType animationType) {
mAnimationType = animationType;
return this;
}
public AnimationUtil setCustomAnimation(Animator animator) {
mCustomAnimator = animator;
return this;
}
public AnimationUtil setDuration(int duration) {
mDuration = duration;
return this;
}
public AnimationUtil setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
return this;
}
public AnimationUtil setTargetView(View targetView) {
mTargetView = targetView;
return this;
}
public void start() {
if (null != mCustomAnimator) {
mCustomAnimator.start();
} else if (null == mTargetView) {
throw new IllegalArgumentException("You must set a target view!");
} else {
startAnimation(mAnimationType);
}
}
private void startAnimation(AnimationType animationType) {
AnimatorSet animatorSet = new AnimatorSet();
switch (animationType) {
case ALPHA:
animatorSet.play(ObjectAnimator.ofFloat(mTargetView, "alpha", 0.7f, 1f));
break;
case SCALE:
animatorSet.playTogether(ObjectAnimator.ofFloat(mTargetView, "scaleX", 0.6f, 1f)
, ObjectAnimator.ofFloat(mTargetView, "scaleY", 0.6f, 1f));
break;
case SLIDE_FROM_BOTTOM:
animatorSet.play(ObjectAnimator.ofFloat(mTargetView, "translationY", mTargetView
.getMeasuredHeight(), 0));
break;
case SLIDE_FROM_LEFT:
animatorSet.play(ObjectAnimator.ofFloat(mTargetView, "translationX", -mTargetView
.getRootView().getWidth(), 0));
break;
case SLIDE_FROM_RIGHT:
animatorSet.play(ObjectAnimator.ofFloat(mTargetView, "translationX", mTargetView
.getRootView().getWidth(), 0));
break;
}
if (null != mInterpolator) {
animatorSet.setInterpolator(mInterpolator);
}
animatorSet.setDuration(mDuration);
animatorSet.start();
}
}

View File

@ -0,0 +1,140 @@
package com.superrecycleview.superlibrary.adapter;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.net.Uri;
import androidx.recyclerview.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Created by super南仔 on 07/28/16. blog: http://supercwn.github.io/ GitHub:
* https://github.com/supercwn
*/
public class BaseViewHolder extends RecyclerView.ViewHolder {
private final SparseArray<View> mViews;
private Context mContext;
public BaseViewHolder(View itemView, Context context) {
super(itemView);
mViews = new SparseArray<>();
mContext = context;
}
public <TView extends View> TView getView(int id) {
View view = mViews.get(id);
if (view == null) {
view = itemView.findViewById(id);
mViews.put(id, view);
}
return (TView) view;
}
public BaseViewHolder setText(int viewId, CharSequence value) {
TextView view = getView(viewId);
view.setText(value);
return this;
}
public BaseViewHolder setImageURI(int viewId, Uri uri) {
ImageView view = getView(viewId);
view.setImageURI(uri);
return this;
}
public BaseViewHolder setTextColor(int viewId, int textColor) {
TextView view = getView(viewId);
view.setTextColor(textColor);
return this;
}
public BaseViewHolder setTextColorRes(int viewId, int textColorRes) {
TextView view = getView(viewId);
view.setTextColor(mContext.getResources().getColor(textColorRes));
return this;
}
public BaseViewHolder setImageResource(int viewId, int imageResId) {
ImageView view = getView(viewId);
view.setImageResource(imageResId);
return this;
}
public BaseViewHolder setBackgroundColor(int viewId, int color) {
View view = getView(viewId);
view.setBackgroundColor(color);
return this;
}
public BaseViewHolder setBackgroundResource(int viewId, int backgroundRes) {
View view = getView(viewId);
view.setBackgroundResource(backgroundRes);
return this;
}
public BaseViewHolder setVisible(int viewId, boolean visible) {
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.GONE);
return this;
}
public BaseViewHolder setTypeface(Typeface typeface, int... viewIds) {
for (int viewId : viewIds) {
TextView view = getView(viewId);
view.setTypeface(typeface);
view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
}
return this;
}
public BaseViewHolder setOnClickListener(int viewId, View.OnClickListener listener) {
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
public BaseViewHolder setOnTouchListener(int viewId, View.OnTouchListener listener) {
View view = getView(viewId);
view.setOnTouchListener(listener);
return this;
}
public BaseViewHolder setOnLongClickListener(int viewId, View.OnLongClickListener listener) {
View view = getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
public BaseViewHolder setOnClickListener(int viewId,
SuperBaseAdapter.OnItemChildClickListener listener) {
View view = getView(viewId);
listener.mViewHolder = this;
view.setOnClickListener(listener);
return this;
}
/**
* Sets the on longClick listener of the view.
*
* @param viewId
* @param listener
* @return
*/
public BaseViewHolder setOnLongClickListener(int viewId,
SuperBaseAdapter.OnItemChildLongClickListener listener) {
View view = getView(viewId);
listener.mViewHolder = this;
view.setOnLongClickListener(listener);
return this;
}
public BaseViewHolder setTag(int viewId, Object tag) {
View view = getView(viewId);
view.setTag(tag);
return this;
}
}

View File

@ -0,0 +1,479 @@
package com.superrecycleview.superlibrary.adapter;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.animation.Animator;
import android.content.Context;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by super南仔 on 07/28/16. blog: http://supercwn.github.io/ GitHub:
* https://github.com/supercwn
*/
public abstract class SuperBaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
private static final String TAG = "SuperBaseAdapter";
public static class VIEW_TYPE {
public static final int HEADER = 0x0010;
public static final int FOOTER = 0x0011;
}
/**
* Base config
*/
public List<T> mData;
private Context mContext;
private LayoutInflater mInflater;
/**
* Listener
*/
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private OnRecyclerViewItemChildClickListener mChildClickListener;
private OnRecyclerViewItemChildLongClickListener mChildLongClickListener;
/**
* View type
*/
private Map<Integer, Integer> layoutIdMap, viewTypeMap;
private int mCurrentViewTypeValue = 0x0107;
/**
* Animation
*/
private AnimationType mAnimationType;
private int mAnimationDuration = 300;
private boolean showItemAnimationEveryTime = false;
private Interpolator mItemAnimationInterpolator;
private CustomAnimator mCustomAnimator;
private int mLastItemPosition = -1;
/**
* header and footer
*/
private LinearLayout mHeaderLayout;
private LinearLayout mFooterLayout;
private LinearLayout mCopyHeaderLayout = null;
private LinearLayout mCopyFooterLayout = null;
public SuperBaseAdapter(Context context) {
this(context, null);
}
public SuperBaseAdapter(Context context, List<T> data) {
mData = null == data ? new ArrayList<T>() : data;
layoutIdMap = new HashMap<>();
viewTypeMap = new HashMap<>();
mContext = context;
mInflater = LayoutInflater.from(context);
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseViewHolder baseViewHolder;
switch (viewType) {
case VIEW_TYPE.HEADER:// header
baseViewHolder = new BaseViewHolder(mHeaderLayout, mContext);
break;
case VIEW_TYPE.FOOTER:// footer
baseViewHolder = new BaseViewHolder(mFooterLayout, mContext);
break;
default:
baseViewHolder = new BaseViewHolder(mInflater.inflate(layoutIdMap.get(viewType),
parent, false), mContext);
initItemClickListener(baseViewHolder);
break;
}
return baseViewHolder;
}
@Override
public int getItemViewType(int position) {
if (position < getHeaderViewCount()) {
return VIEW_TYPE.HEADER;
} else if (position >= mData.size() + getHeaderViewCount()) {
return VIEW_TYPE.FOOTER;
} else {
int currentPosition = position - getHeaderViewCount();
int currentLayoutId = getItemViewLayoutId(currentPosition, mData.get(currentPosition));
if (!viewTypeMap.containsKey(currentLayoutId)) {
mCurrentViewTypeValue++;
viewTypeMap.put(currentLayoutId, mCurrentViewTypeValue);
layoutIdMap.put(viewTypeMap.get(currentLayoutId), currentLayoutId);
}
return viewTypeMap.get(currentLayoutId);
}
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
switch (getItemViewType(position)) {
case VIEW_TYPE.HEADER:
// Do nothing
break;
case VIEW_TYPE.FOOTER:
// Do nothing
break;
default:
convert(holder, getItem(position - getHeaderViewCount()), position -
getHeaderViewCount());
addAnimation(holder);
break;
}
}
protected final void addAnimation(final BaseViewHolder holder) {
int currentPosition = holder.getAdapterPosition();
if (null != mCustomAnimator) {
mCustomAnimator.getAnimator(holder.itemView).setDuration(mAnimationDuration).start();
} else if (null != mAnimationType) {
if (showItemAnimationEveryTime || currentPosition > mLastItemPosition) {
new AnimationUtil()
.setAnimationType(mAnimationType)
.setTargetView(holder.itemView)
.setDuration(mAnimationDuration)
.setInterpolator(mItemAnimationInterpolator)
.start();
mLastItemPosition = currentPosition;
}
}
}
/**
* init the baseViewHolder to register mOnItemClickListener and
* mOnItemLongClickListener
*
* @param holder
*/
protected final void initItemClickListener(final BaseViewHolder holder) {
if (null != mOnItemClickListener) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final int position = holder.getAdapterPosition() - getHeaderViewCount();
mOnItemClickListener.onItemClick(view, position);
}
});
}
if (null != mOnItemLongClickListener) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
final int position = holder.getAdapterPosition() - getHeaderViewCount();
mOnItemLongClickListener.onItemLongClick(v, position);
return true;
}
});
}
}
/**
* Base api
*/
protected abstract void convert(BaseViewHolder holder, T item, int position);
protected abstract int getItemViewLayoutId(int position, T item);
protected T getItem(int position) {
return mData.get(position);
}
@Override
public int getItemCount() {
return mData.size() + getHeaderViewCount() + getFooterViewCount();
}
/**
* Listener api
*/
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
mOnItemLongClickListener = onItemLongClickListener;
}
/**
* Register a callback to be invoked when childView in this AdapterView has
* been clicked and held {@link OnRecyclerViewItemChildClickListener}
*
* @param childClickListener The callback that will run
*/
public void setOnItemChildClickListener(
OnRecyclerViewItemChildClickListener childClickListener) {
this.mChildClickListener = childClickListener;
}
public class OnItemChildClickListener implements View.OnClickListener {
public RecyclerView.ViewHolder mViewHolder;
@Override
public void onClick(View v) {
if (mChildClickListener != null)
mChildClickListener.onItemChildClick(SuperBaseAdapter.this, v,
mViewHolder.getLayoutPosition() - getHeaderViewCount());
}
}
/**
* Register a callback to be invoked when childView in this AdapterView has
* been longClicked and held
* {@link OnRecyclerViewItemChildLongClickListener}
*
* @param childLongClickListener The callback that will run
*/
public void setOnItemChildLongClickListener(
OnRecyclerViewItemChildLongClickListener childLongClickListener) {
this.mChildLongClickListener = childLongClickListener;
}
public class OnItemChildLongClickListener implements View.OnLongClickListener {
public RecyclerView.ViewHolder mViewHolder;
@Override
public boolean onLongClick(View v) {
if (mChildLongClickListener != null) {
return mChildLongClickListener.onItemChildLongClick(SuperBaseAdapter.this, v,
mViewHolder.getLayoutPosition() - getHeaderViewCount());
}
return false;
}
}
/**
* Animation api
*/
public void setItemAnimation(AnimationType animationType) {
mAnimationType = animationType;
}
public void setItemAnimationDuration(int animationDuration) {
mAnimationDuration = animationDuration;
}
public void setItemAnimationInterpolator(Interpolator animationInterpolator) {
mItemAnimationInterpolator = animationInterpolator;
}
public void setShowItemAnimationEveryTime(boolean showItemAnimationEveryTime) {
this.showItemAnimationEveryTime = showItemAnimationEveryTime;
}
public void setCustomItemAnimator(CustomAnimator customAnimator) {
mCustomAnimator = customAnimator;
}
/**
* Header and footer api
*/
public LinearLayout getHeaderLayout() {
return mHeaderLayout;
}
public LinearLayout getFooterLayout() {
return mFooterLayout;
}
public void addHeaderView(View header) {
addHeaderView(header, -1);
}
public void addHeaderView(View header, int index) {
if (mHeaderLayout == null) {
if (mCopyHeaderLayout == null) {
mHeaderLayout = new LinearLayout(header.getContext());
mHeaderLayout.setOrientation(LinearLayout.VERTICAL);
mHeaderLayout
.setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
mCopyHeaderLayout = mHeaderLayout;
} else {
mHeaderLayout = mCopyHeaderLayout;
}
}
index = index >= mHeaderLayout.getChildCount() ? -1 : index;
mHeaderLayout.addView(header, index);
this.notifyDataSetChanged();
}
public void addFooterView(View footer) {
addFooterView(footer, -1);
}
public void addFooterView(View footer, int index) {
if (mFooterLayout == null) {
if (mCopyFooterLayout == null) {
mFooterLayout = new LinearLayout(footer.getContext());
mFooterLayout.setOrientation(LinearLayout.VERTICAL);
mFooterLayout
.setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
mCopyFooterLayout = mFooterLayout;
} else {
mFooterLayout = mCopyFooterLayout;
}
}
index = index >= mFooterLayout.getChildCount() ? -1 : index;
mFooterLayout.addView(footer, index);
this.notifyDataSetChanged();
}
public void removeHeaderView(View header) {
if (mHeaderLayout == null)
return;
mHeaderLayout.removeView(header);
if (mHeaderLayout.getChildCount() == 0) {
mHeaderLayout = null;
}
this.notifyDataSetChanged();
}
public void removeFooterView(View footer) {
if (mFooterLayout == null)
return;
mFooterLayout.removeView(footer);
if (mFooterLayout.getChildCount() == 0) {
mFooterLayout = null;
}
this.notifyDataSetChanged();
}
public void removeAllHeaderView() {
if (mHeaderLayout == null)
return;
mHeaderLayout.removeAllViews();
mHeaderLayout = null;
}
public void removeAllFooterView() {
if (mFooterLayout == null)
return;
mFooterLayout.removeAllViews();
mFooterLayout = null;
}
public int getHeaderViewCount() {
return null == mHeaderLayout ? 0 : 1;
}
public int getFooterViewCount() {
return null == mFooterLayout ? 0 : 1;
}
/**
* Some interface
*/
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
public interface OnRecyclerViewItemChildClickListener {
void onItemChildClick(SuperBaseAdapter adapter, View view, int position);
}
public interface OnRecyclerViewItemChildLongClickListener {
boolean onItemChildLongClick(SuperBaseAdapter adapter, View view, int position);
}
public interface CustomAnimator {
Animator getAnimator(View itemView);
}
private SpanSizeLookup mSpanSizeLookup;
public interface SpanSizeLookup {
int getSpanSize(GridLayoutManager gridLayoutManager, int position);
}
/**
* @param spanSizeLookup instance to be used to query number of spans
* occupied by each item
*/
public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
this.mSpanSizeLookup = spanSizeLookup;
}
// 处理GridLayoutManager添加头部以及添加底部的错误
@Override
public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = getItemViewType(position - 1);
if (mSpanSizeLookup == null)
return (type == VIEW_TYPE.HEADER || type == VIEW_TYPE.FOOTER)
? gridManager.getSpanCount() : 1;
else
return (type == VIEW_TYPE.HEADER || type == VIEW_TYPE.FOOTER)
? gridManager.getSpanCount()
: mSpanSizeLookup.getSpanSize(gridManager,
position - getHeaderViewCount());
}
});
}
}
// 处理StaggeredGridLayoutManager添加头部以及添加底部的
@Override
public void onViewAttachedToWindow(BaseViewHolder holder) {
super.onViewAttachedToWindow(holder);
super.onViewAttachedToWindow(holder);
int type = holder.getItemViewType();
if (type == VIEW_TYPE.HEADER || type == VIEW_TYPE.FOOTER) {
if (holder.itemView
.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView
.getLayoutParams();
params.setFullSpan(true);
}
}
}
/**
* * 刷新数据,初始化数据
*/
public void setDatas(List<T> list) {
if (this.mData != null) {
if (null != list) {
List<T> temp = new ArrayList<>();
temp.addAll(list);
this.mData.clear();
this.mData.addAll(temp);
} else {
this.mData.clear();
}
} else {
this.mData = list;
}
notifyDataSetChanged();
}
}

View File

@ -0,0 +1,202 @@
package com.superrecycleview.superlibrary.adapter;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.view.MotionEventCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import android.view.MotionEvent;
import android.view.View;
import com.superrecycleview.superlibrary.R;
import com.superrecycleview.superlibrary.callback.OnItemDragListener;
import java.util.Collections;
import java.util.List;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public abstract class SuperBaseDragAdapter<T> extends SuperBaseAdapter<T> {
private static final int NO_TOGGLE_VIEW = 0;
private int mToggleViewId = NO_TOGGLE_VIEW;
private ItemTouchHelper mItemTouchHelper;
private boolean itemDragEnabled = false;
private boolean itemSwipeEnabled = false;
private OnItemDragListener mOnItemDragListener;
private boolean mDragOnLongPress = true;
private View.OnTouchListener mOnToggleViewTouchListener;
private View.OnLongClickListener mOnToggleViewLongClickListener;
public SuperBaseDragAdapter(Context context, List<T> data) {
super(context, data);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
int viewType = holder.getItemViewType();
if (mItemTouchHelper != null && itemDragEnabled ) {
if (mToggleViewId != NO_TOGGLE_VIEW) {
View toggleView = holder.getView(mToggleViewId);
if (toggleView != null) {
toggleView.setTag(R.id.BaseQuickAdapter_viewholder_support, holder);
if (mDragOnLongPress) {
toggleView.setOnLongClickListener(mOnToggleViewLongClickListener);
} else {
toggleView.setOnTouchListener(mOnToggleViewTouchListener);
}
}
} else {
holder.itemView.setTag(R.id.BaseQuickAdapter_viewholder_support, holder);
holder.itemView.setOnLongClickListener(mOnToggleViewLongClickListener);
}
}
}
/**
* Set the toggle view's id which will trigger drag event.
* If the toggle view id is not set, drag event will be triggered when the item is long pressed.
*
* @param toggleViewId the toggle view's id
*/
public void setToggleViewId(int toggleViewId) {
mToggleViewId = toggleViewId;
}
/**
* Set the drag event should be trigger on long press.
* Work when the toggleViewId has been set.
*
* @param longPress by default is true.
*/
public void setToggleDragOnLongPress(boolean longPress) {
mDragOnLongPress = longPress;
if (mDragOnLongPress) {
mOnToggleViewTouchListener = null;
mOnToggleViewLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mItemTouchHelper != null && itemDragEnabled) {
mItemTouchHelper.startDrag((RecyclerView.ViewHolder) v.getTag(R.id.BaseQuickAdapter_viewholder_support));
}
return true;
}
};
} else {
mOnToggleViewTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN
&& !mDragOnLongPress) {
if (mItemTouchHelper != null && itemDragEnabled) {
mItemTouchHelper.startDrag((RecyclerView.ViewHolder) v.getTag(R.id.BaseQuickAdapter_viewholder_support));
}
return true;
} else {
return false;
}
}
};
mOnToggleViewLongClickListener = null;
}
}
/**
* Enable drag items.
* Use itemView as the toggleView when long pressed.
*
* @param itemTouchHelper {@link ItemTouchHelper}
*/
public void enableDragItem(@NonNull ItemTouchHelper itemTouchHelper) {
enableDragItem(itemTouchHelper, NO_TOGGLE_VIEW, true);
}
/**
* Enable drag items. Use the specified view as toggle.
*
* @param itemTouchHelper {@link ItemTouchHelper}
* @param toggleViewId The toggle view's id.
* @param dragOnLongPress If true the drag event will be trigger on long press, otherwise on touch down.
*/
public void enableDragItem(@NonNull ItemTouchHelper itemTouchHelper, int toggleViewId, boolean dragOnLongPress) {
itemDragEnabled = true;
mItemTouchHelper = itemTouchHelper;
setToggleViewId(toggleViewId);
setToggleDragOnLongPress(dragOnLongPress);
}
/**
* Disable drag items.
*/
public void disableDragItem() {
itemDragEnabled = false;
mItemTouchHelper = null;
}
public boolean isItemDraggable() {
return itemDragEnabled;
}
/**
* <p>Enable swipe items.</p>
*/
public void enableSwipeItem() {
itemSwipeEnabled = true;
}
public void disableSwipeItem() {
itemSwipeEnabled = false;
}
public boolean isItemSwipeEnable() {
return itemSwipeEnabled;
}
/**
* @param onItemDragListener Register a callback to be invoked when drag event happen.
*/
public void setOnItemDragListener(OnItemDragListener onItemDragListener) {
mOnItemDragListener = onItemDragListener;
}
public int getViewHolderPosition(RecyclerView.ViewHolder viewHolder) {
return viewHolder.getAdapterPosition() - getHeaderViewCount() -1;
}
public void onItemDragStart(RecyclerView.ViewHolder viewHolder) {
if (mOnItemDragListener != null && itemDragEnabled) {
mOnItemDragListener.onItemDragStart(viewHolder, getViewHolderPosition(viewHolder));
}
}
public void onItemDragMoving(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
int from = getViewHolderPosition(source);
int to = getViewHolderPosition(target);
if (from < to) {
for (int i = from; i < to; i++) {
Collections.swap(mData, i, i + 1);
}
} else {
for (int i = from; i > to; i--) {
Collections.swap(mData, i, i - 1);
}
}
notifyItemMoved(source.getAdapterPosition(), target.getAdapterPosition());
if (mOnItemDragListener != null && itemDragEnabled) {
mOnItemDragListener.onItemDragMoving(source, from, target, to);
}
}
public void onItemDragEnd(RecyclerView.ViewHolder viewHolder) {
if (mOnItemDragListener != null && itemDragEnabled) {
mOnItemDragListener.onItemDragEnd(viewHolder, getViewHolderPosition(viewHolder));
}
}
}

View File

@ -0,0 +1,238 @@
package com.superrecycleview.superlibrary.adapter;
import android.content.Context;
import androidx.collection.SparseArrayCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* 介绍一个给RecyclerView添加HeaderView FooterView的装饰Adapter类 重点哦~
* RecyclerView的HeaderView将可以被系统回收不像老版的HeaderView是一个强引用在内存里 作者zhangxutong
* 邮箱zhangxutong@imcoming.com 时间: 2016/8/2.
*/
public abstract class SuperHeaderAndFooterAdapter
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int BASE_ITEM_TYPE_HEADER = 1000000;// headerview的viewtype基准值
private static final int BASE_ITEM_TYPE_FOOTER = 2000000;// footerView的ViewType基准值
// 存放HeaderViews的layoudID和data,key是viewTypevalue 是 layoudID和data
// 在createViewHOlder里根据layoutId创建UI,
// 在onbindViewHOlder里依据这个data渲染UI同时也将layoutId回传出去用于判断何种Header
private SparseArrayCompat<SparseArrayCompat> mHeaderDatas = new SparseArrayCompat<SparseArrayCompat>();
private SparseArrayCompat<View> mFooterViews = new SparseArrayCompat<>();// 存放FooterViews,key是viewType
protected RecyclerView.Adapter mInnerAdapter;// 内部的的普通Adapter
private Context mContext;
public SuperHeaderAndFooterAdapter(Context mContext,
RecyclerView.Adapter mInnerAdapter) {
this.mInnerAdapter = mInnerAdapter;
this.mContext = mContext;
}
public int getHeaderViewCount() {
return mHeaderDatas.size();
}
public int getFooterViewCount() {
return mFooterViews.size();
}
private int getInnerItemCount() {
return mInnerAdapter != null ? mInnerAdapter.getItemCount() : 0;
}
/**
* 传入position 判断是否是headerview
*
* @param position
* @return
*/
public boolean isHeaderViewPos(int position) {// 举例, 2 个头pos 0 1true 2+
return getHeaderViewCount() > position;
}
/**
* 传入postion判断是否是footerview
*
* @param position
* @return
*/
public boolean isFooterViewPos(int position) {// 举例, 2个头2个innerpos 0 1 2
// 3// ,false,4+true
return position >= getHeaderViewCount() + getInnerItemCount();
}
/**
* 添加HeaderView
*
* @param layoutId headerView 的LayoutId
* @param data headerView 的data(可能多种不同类型的header 只能用Object了)
*/
public void addHeaderView(int layoutId, Object data) {
SparseArrayCompat headerContainer = new SparseArrayCompat();
headerContainer.put(layoutId, data);
mHeaderDatas.put(mHeaderDatas.size() + BASE_ITEM_TYPE_HEADER, headerContainer);
}
/**
* 设置(更新)某个layoutId的HeaderView的数据
*
* @param layoutId
* @param data
*/
public void setHeaderView(int layoutId, Object data) {
boolean isFinded = false;
for (int i = 0; i < mHeaderDatas.size(); i++) {
SparseArrayCompat sparse = mHeaderDatas.valueAt(i);
if (layoutId == sparse.keyAt(0)) {
sparse.setValueAt(0, data);
isFinded = true;
}
}
if (!isFinded) {// 没发现 说明是addHeaderView
addHeaderView(layoutId, data);
}
}
/**
* 设置某个位置的HeaderView
*
* @param headerPos 从0开始如果pos过大 就是addHeaderview
* @param layoutId
* @param data
*/
public void setHeaderView(int headerPos, int layoutId, Object data) {
if (mHeaderDatas.size() > headerPos) {
SparseArrayCompat headerContainer = new SparseArrayCompat();
headerContainer.put(layoutId, data);
mHeaderDatas.setValueAt(headerPos, headerContainer);
} else if (mHeaderDatas.size() == headerPos) {// 调用addHeaderView
addHeaderView(layoutId, data);
} else {
//
addHeaderView(layoutId, data);
}
}
/**
* 添加FooterView
*
* @param v
*/
public void addFooterView(View v) {
mFooterViews.put(mFooterViews.size() + BASE_ITEM_TYPE_FOOTER, v);
}
/**
* 清空HeaderView数据
*/
public void clearHeaderView() {
mHeaderDatas.clear();
}
public void clearFooterView() {
mFooterViews.clear();
}
public void setFooterView(View v) {
clearFooterView();
addFooterView(v);
}
@Override
public int getItemViewType(int position) {
if (isHeaderViewPos(position)) {
return mHeaderDatas.keyAt(position);
} else if (isFooterViewPos(position)) {// 举例header 2 innter 2
// 0123都不是4才是4-2-2 = 0ok。
return mFooterViews.keyAt(position - getHeaderViewCount() - getInnerItemCount());
}
return super.getItemViewType(position - getHeaderViewCount());
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (mHeaderDatas.get(viewType) != null) {
View itemView = LayoutInflater.from(mContext).inflate(
mHeaderDatas.get(viewType).keyAt(0), parent,
false);
return new BaseViewHolder(itemView, mContext);
} else if (mFooterViews.get(viewType) != null) {// 不为空说明是footerview
return new BaseViewHolder(mFooterViews.get(viewType), mContext);
}
return mInnerAdapter.onCreateViewHolder(parent, viewType);
}
protected abstract void onBindHeaderHolder(BaseViewHolder holder, int headerPos, int layoutId,
Object object);// 多回传一个layoutId出去用于判断是第几个headerview
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderViewPos(position)) {
int layoutId = mHeaderDatas.get(getItemViewType(position)).keyAt(0);
onBindHeaderHolder((BaseViewHolder) holder, position, layoutId,
mHeaderDatas.get(getItemViewType(position)).get(layoutId));
return;
} else if (isFooterViewPos(position)) {
return;
}
// 举例子2个header0 1是头2是开始2-2 = 0
mInnerAdapter.onBindViewHolder(holder, position - getHeaderViewCount());
}
@Override
public int getItemCount() {
return getInnerItemCount() + getHeaderViewCount() + getFooterViewCount();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
mInnerAdapter.onAttachedToRecyclerView(recyclerView);
// 为了兼容GridLayout
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
final GridLayoutManager.SpanSizeLookup spanSizeLookup = gridLayoutManager
.getSpanSizeLookup();
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int viewType = getItemViewType(position);
if (mHeaderDatas.get(viewType) != null) {
return gridLayoutManager.getSpanCount();
} else if (mFooterViews.get(viewType) != null) {
return gridLayoutManager.getSpanCount();
}
if (spanSizeLookup != null)
return spanSizeLookup.getSpanSize(position);
return 1;
}
});
gridLayoutManager.setSpanCount(gridLayoutManager.getSpanCount());
}
}
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
mInnerAdapter.onViewAttachedToWindow(holder);
int position = holder.getLayoutPosition();
if (isHeaderViewPos(position) || isFooterViewPos(position)) {
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null
&& lp instanceof StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);
}
}
}
}

View File

@ -0,0 +1,16 @@
package com.superrecycleview.superlibrary.callback;
/**
* 介绍:分类悬停的接口 作者zhangxutong 邮箱mcxtzhang@163.com
* 主页http://blog.csdn.net/zxt0601 时间: 2016/11/7.
*/
public interface ISuspensionInterface {
// 是否需要显示悬停title
boolean isShowSuspension();
// 悬停的title
String getSuspensionTag();
}

View File

@ -0,0 +1,149 @@
package com.superrecycleview.superlibrary.callback;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.superrecycleview.superlibrary.R;
import com.superrecycleview.superlibrary.adapter.SuperBaseAdapter;
import com.superrecycleview.superlibrary.adapter.SuperBaseDragAdapter;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public class ItemDragCallback extends ItemTouchHelper.Callback {
// private static final String TAG = ItemDragCallback.class.getSimpleName();
SuperBaseDragAdapter mAdapter;
float mMoveThreshold = 0.1f;
float mSwipeThreshold = 0.7f;
int mDragMoveFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int mSwipeMoveFlags = ItemTouchHelper.END;
public ItemDragCallback(SuperBaseDragAdapter adapter) {
mAdapter = adapter;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return mAdapter.isItemSwipeEnable();
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG
&& !isViewCreateByAdapter(viewHolder)) {
mAdapter.onItemDragStart(viewHolder);
viewHolder.itemView.setTag(R.id.BaseQuickAdapter_dragging_support, true);
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (isViewCreateByAdapter(viewHolder)) {
return;
}
if (viewHolder.itemView.getTag(R.id.BaseQuickAdapter_dragging_support) != null
&& (Boolean)viewHolder.itemView.getTag(R.id.BaseQuickAdapter_dragging_support)) {
mAdapter.onItemDragEnd(viewHolder);
viewHolder.itemView.setTag(R.id.BaseQuickAdapter_dragging_support, false);
}
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (isViewCreateByAdapter(viewHolder)) {
return makeMovementFlags(0, 0);
}
return makeMovementFlags(mDragMoveFlags, mSwipeMoveFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
return source.getItemViewType() == target.getItemViewType();
}
@Override
public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder source, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
mAdapter.onItemDragMoving(source, target);
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
@Override
public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) {
return mMoveThreshold;
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return mSwipeThreshold;
}
/**
* Set the fraction that the user should move the View to be considered as swiped.
* The fraction is calculated with respect to RecyclerView's bounds.
* <p>
* Default value is .5f, which means, to swipe a View, user must move the View at least
* half of RecyclerView's width or height, depending on the swipe direction.
*
* @param swipeThreshold A float value that denotes the fraction of the View size. Default value
* is .8f .
*/
public void setSwipeThreshold(float swipeThreshold) {
mSwipeThreshold = swipeThreshold;
}
/**
* Set the fraction that the user should move the View to be considered as it is
* dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views
* below it for a possible drop.
*
* @param moveThreshold A float value that denotes the fraction of the View size. Default value is
* .1f .
*/
public void setMoveThreshold(float moveThreshold) {
mMoveThreshold = moveThreshold;
}
/**
* <p>Set the drag movement direction.</p>
* <p>The value should be ItemTouchHelper.UP, ItemTouchHelper.DOWN, ItemTouchHelper.LEFT, ItemTouchHelper.RIGHT or their combination.</p>
* You can combine them like ItemTouchHelper.UP | ItemTouchHelper.DOWN, it means that the item could only move up and down when dragged.
* @param dragMoveFlags the drag movement direction. Default value is ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT.
*/
public void setDragMoveFlags(int dragMoveFlags) {
mDragMoveFlags = dragMoveFlags;
}
/**
* <p>Set the swipe movement direction.</p>
* <p>The value should be ItemTouchHelper.START, ItemTouchHelper.END or their combination.</p>
* You can combine them like ItemTouchHelper.START | ItemTouchHelper.END, it means that the item could swipe to both left or right.
* @param swipeMoveFlags the swipe movement direction. Default value is ItemTouchHelper.END.
*/
public void setSwipeMoveFlags(int swipeMoveFlags) {
mSwipeMoveFlags = swipeMoveFlags;
}
private boolean isViewCreateByAdapter(RecyclerView.ViewHolder viewHolder) {
int type = viewHolder.getItemViewType();
return type == SuperBaseAdapter.VIEW_TYPE.HEADER || type == SuperBaseAdapter.VIEW_TYPE.FOOTER;
}
}

View File

@ -0,0 +1,15 @@
package com.superrecycleview.superlibrary.callback;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public interface OnItemDragListener {
void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos);
void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to);
void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos);
}

View File

@ -0,0 +1,300 @@
package com.superrecycleview.superlibrary.callback;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import java.util.List;
/**
* 介绍分类、悬停的Decoration 作者zhangxutong 邮箱mcxtzhang@163.com
* 主页http://blog.csdn.net/zxt0601 时间: 2016/11/7.
*/
public class SuspensionDecoration extends RecyclerView.ItemDecoration {
private List<? extends ISuspensionInterface> mDatas;
private Paint mPaint;
private Rect mBounds;// 用于存放测量文字Rect
private LayoutInflater mInflater;
private int mTitleHeight;// title的高
private static int COLOR_TITLE_BG = Color.parseColor("#FFDFDFDF");
private static int COLOR_TITLE_FONT = Color.parseColor("#FF999999");
private static int mTitleFontSize;// title字体大小
private int mHeaderViewCount = 0;
public SuspensionDecoration(Context context, List<? extends ISuspensionInterface> datas) {
super();
mDatas = datas;
mPaint = new Paint();
mBounds = new Rect();
mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30,
context.getResources().getDisplayMetrics());
mTitleFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,
context.getResources().getDisplayMetrics());
mPaint.setTextSize(mTitleFontSize);
mPaint.setAntiAlias(true);
mInflater = LayoutInflater.from(context);
}
public SuspensionDecoration setmTitleHeight(int mTitleHeight) {
this.mTitleHeight = mTitleHeight;
return this;
}
public SuspensionDecoration setColorTitleBg(int colorTitleBg) {
COLOR_TITLE_BG = colorTitleBg;
return this;
}
public SuspensionDecoration setColorTitleFont(int colorTitleFont) {
COLOR_TITLE_FONT = colorTitleFont;
return this;
}
public SuspensionDecoration setTitleFontSize(int mTitleFontSize) {
mPaint.setTextSize(mTitleFontSize);
return this;
}
public SuspensionDecoration setDatas(List<? extends ISuspensionInterface> mDatas) {
this.mDatas = mDatas;
return this;
}
public int getHeaderViewCount() {
return mHeaderViewCount;
}
public SuspensionDecoration setHeaderViewCount(int headerViewCount) {
mHeaderViewCount = headerViewCount;
return this;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
int position = params.getViewLayoutPosition();
position -= getHeaderViewCount();
// pos为1size为11>0? true
if (mDatas == null || mDatas.isEmpty() || position > mDatas.size() - 1 || position < 0
|| !mDatas.get(position).isShowSuspension()) {
continue;// 越界
}
// 我记得Rv的item position在重置时可能为-1.保险点判断一下吧
if (position > -1) {
if (position == 0) {// 等于0肯定要有title的
drawTitleArea(c, left, right, child, params, position);
} else {// 其他的通过判断
if (null != mDatas.get(position).getSuspensionTag()
&& !mDatas.get(position).getSuspensionTag()
.equals(mDatas.get(position - 1).getSuspensionTag())) {
// 不为空 且跟前一个tag不一样了说明是新的分类也要title
drawTitleArea(c, left, right, child, params, position);
} else {
// none
}
}
}
}
}
/**
* 绘制Title区域背景和文字的方法
*
* @param c
* @param left
* @param right
* @param child
* @param params
* @param position
*/
private void drawTitleArea(Canvas c, int left, int right, View child,
RecyclerView.LayoutParams params, int position) {// 最先调用,绘制在最下层
mPaint.setColor(COLOR_TITLE_BG);
c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right,
child.getTop() - params.topMargin, mPaint);
mPaint.setColor(COLOR_TITLE_FONT);
mPaint.getTextBounds(mDatas.get(position).getSuspensionTag(), 0,
mDatas.get(position).getSuspensionTag().length(), mBounds);
c.drawText(mDatas.get(position).getSuspensionTag(), 40,
child.getTop() - params.topMargin - (mTitleHeight / 2 - mBounds.height() / 2),
mPaint);
}
@Override
public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {// 最后调用
// 绘制在最上层
int pos = ((LinearLayoutManager) (parent.getLayoutManager()))
.findFirstVisibleItemPosition();
pos -= getHeaderViewCount();
// pos为1size为11>0? true
if (mDatas == null || mDatas.isEmpty() || pos > mDatas.size() - 1 || pos < 0
|| !mDatas.get(pos).isShowSuspension()) {
return;// 越界
}
String tag = mDatas.get(pos).getSuspensionTag();
View child = parent.findViewHolderForLayoutPosition(pos + getHeaderViewCount()).itemView;// 出现一个奇怪的bug有时候child为空所以将
// child
// =
// parent.getChildAt(i)。-》
// parent.findViewHolderForLayoutPosition(pos).itemView
boolean flag = false;// 定义一个flagCanvas是否位移过的标志
if ((pos + 1) < mDatas.size()) {// 防止数组越界(一般情况不会出现)
if (null != tag && !tag.equals(mDatas.get(pos + 1).getSuspensionTag())) {// 当前第一个可见的Item的tag不等于其后一个item的tag说明悬浮的View要切换了
Log.d("zxt", "onDrawOver() called with: c = [" + child.getTop());// 当getTop开始变负它的绝对值是第一个可见的Item移出屏幕的距离
if (child.getHeight() + child.getTop() < mTitleHeight) {// 当第一个可见的item在屏幕中还剩的高度小于title区域的高度时我们也该开始做悬浮Title的“交换动画”
c.save();// 每次绘制前 保存当前Canvas状态
flag = true;
// 一种头部折叠起来的视效,个人觉得也还不错~
// 可与123行 c.drawRect 比较只有bottom参数不一样由于 child.getHeight() +
// child.getTop() < mTitleHeight所以绘制区域是在不断的减小有种折叠起来的感觉
// c.clipRect(parent.getPaddingLeft(),
// parent.getPaddingTop(), parent.getRight() -
// parent.getPaddingRight(), parent.getPaddingTop() +
// child.getHeight() + child.getTop());
// 类似饿了么点餐时,商品列表的悬停头部切换“动画效果”
// 上滑时将canvas上移 y为负数 ,所以后面canvas
// 画出来的Rect和Text都上移了有种切换的“动画”感觉
c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);
}
}
}
mPaint.setColor(COLOR_TITLE_BG);
c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(),
parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight,
mPaint);
mPaint.setColor(COLOR_TITLE_FONT);
mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
c.drawText(tag, 40,
parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2),
mPaint);
if (flag)
c.restore();// 恢复画布到之前保存的状态
/*
* Button button = new Button(parent.getContext());
* button.setOnClickListener(new View.OnClickListener() {
* @Override public void onClick(View v) {
* Toast.makeText(parent.getContext(), "啊哈",
* Toast.LENGTH_SHORT).show();//即使给View设置了点击事件也是无效的它仅仅draw了 } });
* ViewGroup.LayoutParams params = button.getLayoutParams(); if (params
* == null){ params = new
* ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
* ViewGroup.LayoutParams.WRAP_CONTENT); }
* button.setLayoutParams(params); button.setBackgroundColor(Color.RED);
* button.setText("无哈");
*//*
* button.measure(View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.
* EXACTLY),View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.
* EXACTLY));
*//*
* //必须经过 测量 和 布局View才能被正常的显示出来
* button.measure(View.MeasureSpec.makeMeasureSpec(9999,View.
* MeasureSpec.UNSPECIFIED),View.MeasureSpec.makeMeasureSpec(9999,
* View.MeasureSpec.UNSPECIFIED));
* button.layout(parent.getPaddingLeft(),parent.getPaddingTop(),
* parent.getPaddingLeft()+button.getMeasuredWidth(),parent.
* getPaddingTop()+button.getMeasuredHeight()); button.draw(c);
*/
// inflate一个复杂布局 并draw出来
/*
* View toDrawView = mInflater.inflate(R.layout.header_complex, parent,
* false); int toDrawWidthSpec;//用于测量的widthMeasureSpec int
* toDrawHeightSpec;//用于测量的heightMeasureSpec
* //拿到复杂布局的LayoutParams如果为空就new一个。 // 后面需要根据这个lp
* 构建toDrawWidthSpectoDrawHeightSpec ViewGroup.LayoutParams lp =
* toDrawView.getLayoutParams(); if (lp == null) { lp = new
* ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
* ViewGroup.LayoutParams.WRAP_CONTENT);//这里是根据复杂布局layout的width
* heightnew一个Lp toDrawView.setLayoutParams(lp); } if (lp.width ==
* ViewGroup.LayoutParams.MATCH_PARENT) {
* //如果是MATCH_PARENT则用父控件能分配的最大宽度和EXACTLY构建MeasureSpec。 toDrawWidthSpec
* = View.MeasureSpec.makeMeasureSpec(parent.getWidth() -
* parent.getPaddingLeft() - parent.getPaddingRight(),
* View.MeasureSpec.EXACTLY); } else if (lp.width ==
* ViewGroup.LayoutParams.WRAP_CONTENT) {
* //如果是WRAP_CONTENT则用父控件能分配的最大宽度和AT_MOST构建MeasureSpec。 toDrawWidthSpec
* = View.MeasureSpec.makeMeasureSpec(parent.getWidth() -
* parent.getPaddingLeft() - parent.getPaddingRight(),
* View.MeasureSpec.AT_MOST); } else {
* //否则则是具体的宽度数值则用这个宽度和EXACTLY构建MeasureSpec。 toDrawWidthSpec =
* View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY);
* } //高度同理 if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
* toDrawHeightSpec =
* View.MeasureSpec.makeMeasureSpec(parent.getHeight() -
* parent.getPaddingTop() - parent.getPaddingBottom(),
* View.MeasureSpec.EXACTLY); } else if (lp.height ==
* ViewGroup.LayoutParams.WRAP_CONTENT) { toDrawHeightSpec =
* View.MeasureSpec.makeMeasureSpec(parent.getHeight() -
* parent.getPaddingTop() - parent.getPaddingBottom(),
* View.MeasureSpec.AT_MOST); } else { toDrawHeightSpec =
* View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY);
* } //依次调用 measure,layout,draw方法将复杂头部显示在屏幕上。
* toDrawView.measure(toDrawWidthSpec, toDrawHeightSpec);
* toDrawView.layout(parent.getPaddingLeft(), parent.getPaddingTop(),
* parent.getPaddingLeft() + toDrawView.getMeasuredWidth(),
* parent.getPaddingTop() + toDrawView.getMeasuredHeight());
* toDrawView.draw(c);
*/
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
// super里会先设置0 0 0 0
super.getItemOffsets(outRect, view, parent, state);
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
position -= getHeaderViewCount();
if (mDatas == null || mDatas.isEmpty() || position > mDatas.size() - 1) {// pos为1size为11>0?
// true
return;// 越界
}
// 我记得Rv的item position在重置时可能为-1.保险点判断一下吧
if (position > -1) {
ISuspensionInterface titleCategoryInterface = mDatas.get(position);
// 等于0肯定要有title的,
// 2016 11 07 add 考虑到headerView 等于0 也不应该有title
// 2016 11 10 add 通过接口里的isShowSuspension() 方法先过滤掉不想显示悬停的item
if (titleCategoryInterface.isShowSuspension()) {
if (position == 0) {
outRect.set(0, mTitleHeight, 0, 0);
} else {// 其他的通过判断
if (null != titleCategoryInterface.getSuspensionTag()
&& !titleCategoryInterface.getSuspensionTag()
.equals(mDatas.get(position - 1).getSuspensionTag())) {
// 不为空 且跟前一个tag不一样了说明是新的分类也要title
outRect.set(0, mTitleHeight, 0, 0);
}
}
}
}
}
}

View File

@ -0,0 +1,39 @@
package com.superrecycleview.superlibrary.recycleview;
import com.google.android.material.appbar.AppBarLayout;
/**
* Created by Jack on 2015/10/19.
*/
public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {
public enum State {
EXPANDED,
COLLAPSED,
IDLE
}
private State mCurrentState = State.IDLE;
@Override
public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
if (i == 0) {
if (mCurrentState != State.EXPANDED) {
onStateChanged(appBarLayout, State.EXPANDED);
}
mCurrentState = State.EXPANDED;
} else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
if (mCurrentState != State.COLLAPSED) {
onStateChanged(appBarLayout, State.COLLAPSED);
}
mCurrentState = State.COLLAPSED;
} else {
if (mCurrentState != State.IDLE) {
onStateChanged(appBarLayout, State.IDLE);
}
mCurrentState = State.IDLE;
}
}
public abstract void onStateChanged(AppBarLayout appBarLayout, State state);
}

View File

@ -0,0 +1,267 @@
package com.superrecycleview.superlibrary.recycleview;
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.superrecycleview.superlibrary.R;
import com.superrecycleview.superlibrary.recycleview.progressindicator.AVLoadingIndicatorView;
import java.util.Date;
/**
* Created by Jack on 2015/10/19.
* update by super南仔
*/
public class ArrowRefreshHeader extends LinearLayout implements BaseRefreshHeader {
private LinearLayout mContainer;
private ImageView mArrowImageView;
private SimpleViewSwitcher mProgressBar;
private TextView mStatusTextView;
private int mState = STATE_NORMAL;
private TextView mHeaderTimeView;
private Animation mRotateUpAnim;
private Animation mRotateDownAnim;
private static final int ROTATE_ANIM_DURATION = 180;
public int mMeasuredHeight;
public ArrowRefreshHeader(Context context) {
super(context);
initView();
}
/**
* @param context
* @param attrs
*/
public ArrowRefreshHeader(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private void initView() {
// 初始情况设置下拉刷新view高度为0
mContainer = (LinearLayout) LayoutInflater.from(getContext()).inflate(
R.layout.listview_header, null);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 0, 0, 0);
this.setLayoutParams(lp);
this.setPadding(0, 0, 0, 0);
addView(mContainer, new LayoutParams(LayoutParams.MATCH_PARENT, 0));
setGravity(Gravity.BOTTOM);
mArrowImageView = (ImageView)findViewById(R.id.listview_header_arrow);
mStatusTextView = (TextView)findViewById(R.id.refresh_status_textview);
//init the progress view
mProgressBar = (SimpleViewSwitcher)findViewById(R.id.listview_header_progressbar);
AVLoadingIndicatorView progressView = new AVLoadingIndicatorView(getContext());
progressView.setIndicatorColor(0xffB5B5B5);
progressView.setIndicatorId(ProgressStyle.BallSpinFadeLoader);
mProgressBar.setView(progressView);
mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
mRotateUpAnim.setFillAfter(true);
mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
mRotateDownAnim.setFillAfter(true);
mHeaderTimeView = (TextView)findViewById(R.id.last_refresh_time);
measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mMeasuredHeight = getMeasuredHeight();
}
public void setProgressStyle(int style) {
if(style == ProgressStyle.SysProgress){
mProgressBar.setView(new ProgressBar(getContext(), null, android.R.attr.progressBarStyle));
}else{
AVLoadingIndicatorView progressView = new AVLoadingIndicatorView(this.getContext());
progressView.setIndicatorColor(0xffB5B5B5);
progressView.setIndicatorId(style);
mProgressBar.setView(progressView);
}
}
public void setArrowImageView(int resid){
mArrowImageView.setImageResource(resid);
}
public void setState(int state) {
if (state == mState) return ;
if (state == STATE_REFRESHING) { // 显示进度
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
} else if(state == STATE_DONE) {
mArrowImageView.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.INVISIBLE);
} else { // 显示箭头图片
mArrowImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.INVISIBLE);
}
switch(state){
case STATE_NORMAL:
if (mState == STATE_RELEASE_TO_REFRESH) {
mArrowImageView.startAnimation(mRotateDownAnim);
}
if (mState == STATE_REFRESHING) {
mArrowImageView.clearAnimation();
}
mStatusTextView.setText(R.string.listview_header_hint_normal);
break;
case STATE_RELEASE_TO_REFRESH:
if (mState != STATE_RELEASE_TO_REFRESH) {
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mRotateUpAnim);
mStatusTextView.setText(R.string.listview_header_hint_release);
}
break;
case STATE_REFRESHING:
mStatusTextView.setText(R.string.refreshing);
break;
case STATE_DONE:
mStatusTextView.setText(R.string.refresh_done);
break;
default:
}
mState = state;
}
public int getState() {
return mState;
}
@Override
public void refreshComplete(){
mHeaderTimeView.setText(friendlyTime(new Date()));
setState(STATE_DONE);
new Handler().postDelayed(new Runnable(){
public void run() {
reset();
}
}, 200);
}
public void setVisibleHeight(int height) {
if (height < 0) height = 0;
LayoutParams lp = (LayoutParams) mContainer .getLayoutParams();
lp.height = height;
mContainer.setLayoutParams(lp);
}
public int getVisibleHeight() {
LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();
return lp.height;
}
@Override
public void onMove(float delta) {
if(getVisibleHeight() > 0 || delta > 0) {
setVisibleHeight((int) delta + getVisibleHeight());
if (mState <= STATE_RELEASE_TO_REFRESH) { // 未处于刷新状态,更新箭头
if (getVisibleHeight() > mMeasuredHeight) {
setState(STATE_RELEASE_TO_REFRESH);
}else {
setState(STATE_NORMAL);
}
}
}
}
@Override
public boolean releaseAction() {
boolean isOnRefresh = false;
int height = getVisibleHeight();
if (height == 0) // not visible.
isOnRefresh = false;
if(getVisibleHeight() > mMeasuredHeight && mState < STATE_REFRESHING){
setState(STATE_REFRESHING);
isOnRefresh = true;
}
// refreshing and header isn't shown fully. do nothing.
if (mState == STATE_REFRESHING && height <= mMeasuredHeight) {
//return;
}
int destHeight = 0; // default: scroll back to dismiss header.
// is refreshing, just scroll back to show all the header.
if (mState == STATE_REFRESHING) {
destHeight = mMeasuredHeight;
}
smoothScrollTo(destHeight);
return isOnRefresh;
}
public void reset() {
smoothScrollTo(0);
new Handler().postDelayed(new Runnable() {
public void run() {
setState(STATE_NORMAL);
}
}, 500);
}
private void smoothScrollTo(int destHeight) {
ValueAnimator animator = ValueAnimator.ofInt(getVisibleHeight(), destHeight);
animator.setDuration(300).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
setVisibleHeight((int) animation.getAnimatedValue());
}
});
animator.start();
}
public static String friendlyTime(Date time) {
//获取time距离当前的秒数
int ct = (int)((System.currentTimeMillis() - time.getTime())/1000);
if(ct == 0) {
return "刚刚";
}
if(ct > 0 && ct < 60) {
return ct + "秒前";
}
if(ct >= 60 && ct < 3600) {
return Math.max(ct / 60,1) + "分钟前";
}
if(ct >= 3600 && ct < 86400)
return ct / 3600 + "小时前";
if(ct >= 86400 && ct < 2592000){ //86400 * 30
int day = ct / 86400 ;
return day + "天前";
}
if(ct >= 2592000 && ct < 31104000) { //86400 * 30
return ct / 2592000 + "月前";
}
return ct / 31104000 + "年前";
}
}

View File

@ -0,0 +1,19 @@
package com.superrecycleview.superlibrary.recycleview;
/**
* Created by Jack on 2015/10/19.
*/
interface BaseRefreshHeader {
int STATE_NORMAL = 0;
int STATE_RELEASE_TO_REFRESH = 1;
int STATE_REFRESHING = 2;
int STATE_DONE = 3;
void onMove(float delta);
boolean releaseAction();
void refreshComplete();
}

View File

@ -0,0 +1,38 @@
package com.superrecycleview.superlibrary.recycleview;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by Jack on 2015/10/19.
*/
public interface ItemTouchHelperAdapter {
/**
* Called when an item has been dragged far enough to trigger a move. This is called every time
* an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
* <br/>
* Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
* adjusting the underlying data to reflect this move.
*
* @param fromPosition The start position of the moved item.
* @param toPosition Then resolved position of the moved item.
*
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
* @see RecyclerView.ViewHolder#getAdapterPosition()
*/
void onItemMove(int fromPosition, int toPosition);
/**
* Called when an item has been dismissed by a swipe.<br/>
* <br/>
* Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after
* adjusting the underlying data to reflect this removal.
*
* @param position The position of the item dismissed.
*
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
* @see RecyclerView.ViewHolder#getAdapterPosition()
*/
void onItemDismiss(int position);
}

View File

@ -0,0 +1,106 @@
package com.superrecycleview.superlibrary.recycleview;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* Created by Jack on 2015/10/19.
*/
public class JellyView extends View implements BaseRefreshHeader{
Path path;
Paint paint;
private int minimumHeight = 0;
private int jellyHeight =0;
public JellyView(Context context) {
super(context);
init();
}
public JellyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public JellyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@SuppressWarnings("unused")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public JellyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
if (isInEditMode()) {
return;
}
path = new Path();
paint = new Paint();
paint.setColor(getContext().getResources().getColor(android.R.color.holo_blue_bright));
paint.setAntiAlias(true);
}
public void setJellyColor(int jellyColor) {
paint.setColor(jellyColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.reset();
path.lineTo(0, minimumHeight);
path.quadTo(getMeasuredWidth() / 2, minimumHeight + jellyHeight, getMeasuredWidth(), minimumHeight);
path.lineTo(getMeasuredWidth(), 0);
canvas.drawPath(path, paint);
}
@Override
public void setMinimumHeight(int minimumHeight) {
this.minimumHeight = minimumHeight;
}
public void setJellyHeight(int ribbonHeight) {
this.jellyHeight = ribbonHeight;
}
@Override
public int getMinimumHeight() {
return minimumHeight;
}
public int getJellyHeight() {
return jellyHeight;
}
@Override
public void refreshComplete(){
}
@Override
public void onMove(float delta) {
jellyHeight = jellyHeight + (int)delta;
Log.i("jellyHeight", "delta = " + delta);
this.invalidate();
}
@Override
public boolean releaseAction() {
return false;
}
}

View File

@ -0,0 +1,96 @@
package com.superrecycleview.superlibrary.recycleview;
import android.content.Context;
import androidx.recyclerview.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.superrecycleview.superlibrary.R;
import com.superrecycleview.superlibrary.recycleview.progressindicator.AVLoadingIndicatorView;
/**
* Created by Jack on 2015/10/19.
* update by super南仔
*/
public class LoadingMoreFooter extends LinearLayout {
private SimpleViewSwitcher progressCon;
public final static int STATE_LOADING = 0;
public final static int STATE_COMPLETE = 1;
public final static int STATE_NOMORE = 2;
private TextView mText;
public LoadingMoreFooter(Context context) {
super(context);
initView();
}
/**
* @param context
* @param attrs
*/
public LoadingMoreFooter(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public void initView(){
setGravity(Gravity.CENTER);
setLayoutParams(new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
progressCon = new SimpleViewSwitcher(getContext());
progressCon.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
AVLoadingIndicatorView progressView = new AVLoadingIndicatorView(this.getContext());
progressView.setIndicatorColor(getResources().getColor(R.color.blue));
progressView.setIndicatorId(ProgressStyle.BallSpinFadeLoader);
progressCon.setView(progressView);
addView(progressCon);
mText = new TextView(getContext());
mText.setText("正在加载...");
mText.setTextColor(getResources().getColor(R.color.blue));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(10,0,0,0 );
mText.setLayoutParams(layoutParams);
addView(mText);
}
public void setProgressStyle(int style) {
if(style == ProgressStyle.SysProgress){
progressCon.setView(new ProgressBar(getContext(), null, android.R.attr.progressBarStyle));
}else{
AVLoadingIndicatorView progressView = new AVLoadingIndicatorView(this.getContext());
progressView.setIndicatorColor(getResources().getColor(R.color.blue));
progressView.setIndicatorId(style);
progressCon.setView(progressView);
}
}
public void setState(int state) {
switch(state) {
case STATE_LOADING:
progressCon.setVisibility(View.VISIBLE);
mText.setText("正在加载");
this.setVisibility(View.VISIBLE);
break;
case STATE_COMPLETE:
mText.setText("正在加载");
this.setVisibility(View.GONE);
break;
case STATE_NOMORE:
mText.setText("没有更多了");
progressCon.setVisibility(View.GONE);
this.setVisibility(View.VISIBLE);
break;
}
}
}

View File

@ -0,0 +1,36 @@
package com.superrecycleview.superlibrary.recycleview;
/**
* Created by Jack on 2015/10/19.
*/
public class ProgressStyle {
public static final int SysProgress=-1;
public static final int BallPulse=0;
public static final int BallGridPulse=1;
public static final int BallClipRotate=2;
public static final int BallClipRotatePulse=3;
public static final int SquareSpin=4;
public static final int BallClipRotateMultiple=5;
public static final int BallPulseRise=6;
public static final int BallRotate=7;
public static final int CubeTransition=8;
public static final int BallZigZag=9;
public static final int BallZigZagDeflect=10;
public static final int BallTrianglePath=11;
public static final int BallScale=12;
public static final int LineScale=13;
public static final int LineScaleParty=14;
public static final int BallScaleMultiple=15;
public static final int BallPulseSync=16;
public static final int BallBeat=17;
public static final int LineScalePulseOut=18;
public static final int LineScalePulseOutRapid=19;
public static final int BallScaleRipple=20;
public static final int BallScaleRippleMultiple=21;
public static final int BallSpinFadeLoader=22;
public static final int LineSpinFadeLoader=23;
public static final int TriangleSkewSpin=24;
public static final int Pacman=25;
public static final int BallGridBeat=26;
public static final int SemiCircleSpin=27;
}

View File

@ -0,0 +1,86 @@
package com.superrecycleview.superlibrary.recycleview;
import android.graphics.Canvas;
import android.graphics.Color;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import android.view.View;
/**
* Created by Jack on 2015/10/19.
*/
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
private final ItemTouchHelperAdapter mAdapter;
private SuperRecyclerView mSrecyclerView;
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter, SuperRecyclerView recyclerView) {
mAdapter = adapter;
this.mSrecyclerView = recyclerView;
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// Enable drag and swipe in both directions
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
// Notify the adapter of the move
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Notify the adapter of the dismissal
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
// Fade out the view as it is swiped out of the parent's bounds
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
View itemView = viewHolder.itemView;
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) itemView.getWidth();
itemView.setAlpha(alpha);
}
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
// Let the view holder know that this item is being moved or dragged
viewHolder.itemView.setBackgroundColor(Color.LTGRAY);
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setAlpha(ALPHA_FULL);
viewHolder.itemView.setBackgroundColor(0);
}
}

View File

@ -0,0 +1,60 @@
package com.superrecycleview.superlibrary.recycleview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by Jack on 2015/10/19.
*/
public class SimpleViewSwitcher extends ViewGroup {
public SimpleViewSwitcher(Context context) {
super(context);
}
public SimpleViewSwitcher(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SimpleViewSwitcher(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = this.getChildCount();
int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
int cw = child.getMeasuredWidth();
// int ch = child.getMeasuredHeight();
maxWidth = child.getMeasuredWidth();
maxHeight = child.getMeasuredHeight();
}
setMeasuredDimension(maxWidth, maxHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
child.layout(0, 0, r - l, b - t);
}
}
}
public void setView(View view) {
if (this.getChildCount() != 0){
this.removeViewAt(0);
}
this.addView(view,0);
}
}

View File

@ -0,0 +1,534 @@
package com.superrecycleview.superlibrary.recycleview;
import android.content.Context;
import com.google.android.material.appbar.AppBarLayout;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import com.superrecycleview.superlibrary.adapter.BaseViewHolder;
import com.superrecycleview.superlibrary.adapter.SuperBaseAdapter;
/**
* Created by super南仔 on 07/28/16. blog: http://supercwn.github.io/ GitHub:
* https://github.com/supercwn
*/
public class SuperRecyclerView extends RecyclerView {
private Context context;
private boolean isLoadingData = false;
private boolean isNoMore = false;
private int mRefreshProgressStyle = ProgressStyle.SysProgress;
private int mLoadingMoreProgressStyle = ProgressStyle.SysProgress;
private WrapAdapter mWrapAdapter;
private float mLastY = -1;
private static final float DRAG_RATE = 3;
private LoadingListener mLoadingListener;
private ArrowRefreshHeader mRefreshHeader;
private View mLoadMoreFootView;
private View mEmptyView;
private boolean refreshEnabled = true;
private boolean loadingMoreEnabled = true;
// 下面的ItemViewType是保留值(ReservedItemViewType),如果用户的adapter与它们重复将会强制抛出异常。不过为了简化,我们检测到重复时对用户的提示是ItemViewType必须小于10000
private static final int TYPE_REFRESH_HEADER = 100000;// 设置一个很大的数字,尽可能避免和用户的adapter冲突
private static final int TYPE_LOADMORE_FOOTER = 100001;
private final RecyclerView.AdapterDataObserver mDataObserver = new DataObserver();
private AppBarStateChangeListener.State appbarState = AppBarStateChangeListener.State.EXPANDED;
public SuperRecyclerView(Context context) {
this(context, null);
}
public SuperRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SuperRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
init();
}
private void init() {
if (refreshEnabled) {
mRefreshHeader = new ArrowRefreshHeader(getContext());
mRefreshHeader.setProgressStyle(mRefreshProgressStyle);
}
LoadingMoreFooter footView = new LoadingMoreFooter(getContext());
footView.setProgressStyle(mLoadingMoreProgressStyle);
mLoadMoreFootView = footView;
mLoadMoreFootView.setVisibility(View.GONE);
}
// 判断是否是SuperRecyclerView保留的itemViewType
private boolean isReservedItemViewType(int itemViewType) {
return itemViewType == TYPE_REFRESH_HEADER || itemViewType == TYPE_LOADMORE_FOOTER;
}
public void setLoadMoreFootView(final View view) {
mLoadMoreFootView = view;
}
public void completeLoadMore() {
isLoadingData = false;
if (mLoadMoreFootView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) mLoadMoreFootView).setState(LoadingMoreFooter.STATE_COMPLETE);
} else {
mLoadMoreFootView.setVisibility(View.GONE);
}
}
public void setNoMore(boolean noMore) {
isLoadingData = false;
isNoMore = noMore;
if (mLoadMoreFootView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) mLoadMoreFootView).setState(
isNoMore ? LoadingMoreFooter.STATE_NOMORE : LoadingMoreFooter.STATE_COMPLETE);
} else {
mLoadMoreFootView.setVisibility(View.GONE);
}
}
public void completeRefresh() {
mRefreshHeader.refreshComplete();
setNoMore(false);
}
public void setRefreshHeader(ArrowRefreshHeader refreshHeader) {
mRefreshHeader = refreshHeader;
}
public void setRefreshEnabled(boolean enabled) {
refreshEnabled = enabled;
}
public void setLoadMoreEnabled(boolean enabled) {
loadingMoreEnabled = enabled;
if (!enabled) {
if (mLoadMoreFootView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) mLoadMoreFootView).setState(LoadingMoreFooter.STATE_COMPLETE);
}
}
}
public void setRefreshProgressStyle(int style) {
mRefreshProgressStyle = style;
if (mRefreshHeader != null) {
mRefreshHeader.setProgressStyle(style);
}
}
public void setLoadingMoreProgressStyle(int style) {
mLoadingMoreProgressStyle = style;
if (mLoadMoreFootView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) mLoadMoreFootView).setProgressStyle(style);
}
}
public void setArrowImageView(int resId) {
if (mRefreshHeader != null) {
mRefreshHeader.setArrowImageView(resId);
}
}
public void setEmptyView(View emptyView) {
this.mEmptyView = emptyView;
mDataObserver.onChanged();
}
public View getEmptyView() {
return mEmptyView;
}
@Override
public void setAdapter(Adapter adapter) {
if (adapter instanceof SuperBaseAdapter) {
mWrapAdapter = new WrapAdapter(adapter);
super.setAdapter(mWrapAdapter);
} else {
super.setAdapter(adapter);
}
adapter.registerAdapterDataObserver(mDataObserver);
mDataObserver.onChanged();
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
if (state == RecyclerView.SCROLL_STATE_IDLE && mLoadingListener != null && !isLoadingData
&& loadingMoreEnabled) {
LayoutManager layoutManager = getLayoutManager();
int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) layoutManager)
.findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
lastVisibleItemPosition = findMax(into);
} else {
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager)
.findLastVisibleItemPosition();
}
if (layoutManager.getChildCount() > 0
&& lastVisibleItemPosition >= layoutManager.getItemCount() - 1
&& layoutManager.getItemCount() > layoutManager.getChildCount() && !isNoMore
&& mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) {
isLoadingData = true;
if (mLoadMoreFootView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) mLoadMoreFootView)
.setState(LoadingMoreFooter.STATE_LOADING);
} else {
mLoadMoreFootView.setVisibility(View.VISIBLE);
}
mLoadingListener.onLoadMore();
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
if (isOnTop() && refreshEnabled
&& appbarState == AppBarStateChangeListener.State.EXPANDED) {
mRefreshHeader.onMove(deltaY / DRAG_RATE);
if (mRefreshHeader.getVisibleHeight() > 0
&& mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) {
return false;
}
}
break;
default:
mLastY = -1; // reset
if (isOnTop() && refreshEnabled
&& appbarState == AppBarStateChangeListener.State.EXPANDED) {
if (mRefreshHeader.releaseAction()) {
if (mLoadingListener != null) {
mLoadingListener.onRefresh();
}
}
}
break;
}
return super.onTouchEvent(ev);
}
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
}
return max;
}
private boolean isOnTop() {
return mRefreshHeader.getParent() != null;
}
private class DataObserver extends RecyclerView.AdapterDataObserver {
@Override
public void onChanged() {
Adapter<?> adapter = getAdapter();
if (adapter != null && mEmptyView != null) {
int emptyCount = 1;
if (refreshEnabled) {
emptyCount++;
}
if (loadingMoreEnabled) {
emptyCount++;
}
if (adapter.getItemCount() == emptyCount) {
mEmptyView.setVisibility(View.VISIBLE);
SuperRecyclerView.this.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
SuperRecyclerView.this.setVisibility(View.VISIBLE);
}
}
if (mWrapAdapter != null) {
mWrapAdapter.notifyDataSetChanged();
}
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mWrapAdapter.notifyItemMoved(fromPosition, toPosition);
}
}
public class WrapAdapter extends SuperBaseAdapter {
private RecyclerView.Adapter adapter;
public WrapAdapter(RecyclerView.Adapter adapter) {
super(context);
this.adapter = adapter;
}
public boolean isHeader(int position) {
return position >= 1 && position < 1;
}
public boolean isFooter(int position) {
if (loadingMoreEnabled) {
return position == getItemCount() - 1;
} else {
return false;
}
}
public boolean isRefreshHeader(int position) {
return position == 0;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_REFRESH_HEADER) {
return new SimpleViewHolder(mRefreshHeader);
} else if (viewType == TYPE_LOADMORE_FOOTER) {
return new SimpleViewHolder(mLoadMoreFootView);
}
return (BaseViewHolder) adapter.onCreateViewHolder(parent,
viewType);
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (isHeader(position) || isRefreshHeader(position)) {
return;
}
int adjPosition = position - 1;
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
adapter.onBindViewHolder(holder, adjPosition);
return;
}
}
}
@Override
public int getItemCount() {
if (loadingMoreEnabled) {
if (adapter != null) {
return adapter.getItemCount() + 2;
} else {
return 2;
}
} else {
if (adapter != null) {
return adapter.getItemCount() + 1;
} else {
return 1;
}
}
}
@Override
public int getItemViewType(int position) {
int adjPosition = position - 1;
if (isReservedItemViewType(adapter.getItemViewType(adjPosition))) {
throw new IllegalStateException(
"XRecyclerView require itemViewType in adapter should be less than 10000 ");
}
if (isRefreshHeader(position)) {
return TYPE_REFRESH_HEADER;
}
if (isFooter(position)) {
return TYPE_LOADMORE_FOOTER;
}
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
return adapter.getItemViewType(adjPosition);
}
}
return 0;
}
@Override
protected void convert(BaseViewHolder holder, Object item, int position) {
// do nothing
}
@Override
protected int getItemViewLayoutId(int position, Object item) {
// do nothing
return 0;
}
@Override
public long getItemId(int position) {
if (adapter != null && position >= 1) {
int adjPosition = position - 1;
if (adjPosition < adapter.getItemCount()) {
return adapter.getItemId(adjPosition);
}
}
return -1;
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return (isHeader(position) || isFooter(position)
|| isRefreshHeader(position))
? gridManager.getSpanCount() : 1;
}
});
}
adapter.onAttachedToRecyclerView(recyclerView);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
adapter.onDetachedFromRecyclerView(recyclerView);
}
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow((BaseViewHolder) holder);
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null
&& lp instanceof StaggeredGridLayoutManager.LayoutParams
&& (isHeader(holder.getLayoutPosition())
|| isRefreshHeader(holder.getLayoutPosition())
|| isFooter(holder.getLayoutPosition()))) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);
}
adapter.onViewAttachedToWindow(holder);
}
@Override
public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
adapter.onViewDetachedFromWindow(holder);
}
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
adapter.onViewRecycled(holder);
}
@Override
public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
return adapter.onFailedToRecycleView(holder);
}
@Override
public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
adapter.unregisterAdapterDataObserver(observer);
}
@Override
public void registerAdapterDataObserver(AdapterDataObserver observer) {
adapter.registerAdapterDataObserver(observer);
}
private class SimpleViewHolder extends BaseViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView, context);
}
}
}
public void setLoadingListener(LoadingListener listener) {
mLoadingListener = listener;
}
public interface LoadingListener {
void onRefresh();
void onLoadMore();
}
public void setRefreshing(boolean refreshing) {
if (refreshing && refreshEnabled && mLoadingListener != null) {
mRefreshHeader.setState(ArrowRefreshHeader.STATE_REFRESHING);
mRefreshHeader.onMove(mRefreshHeader.getMeasuredHeight());
mLoadingListener.onRefresh();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// 解决和CollapsingToolbarLayout冲突的问题
AppBarLayout appBarLayout = null;
ViewParent p = getParent();
while (p != null) {
if (p instanceof CoordinatorLayout) {
break;
}
p = p.getParent();
}
if (p instanceof CoordinatorLayout) {
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) p;
final int childCount = coordinatorLayout.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final View child = coordinatorLayout.getChildAt(i);
if (child instanceof AppBarLayout) {
appBarLayout = (AppBarLayout) child;
break;
}
}
if (appBarLayout != null) {
appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
@Override
public void onStateChanged(AppBarLayout appBarLayout, State state) {
appbarState = state;
}
});
}
}
}
}

View File

@ -0,0 +1,365 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import androidx.annotation.IntDef;
import android.util.AttributeSet;
import android.view.View;
import com.superrecycleview.superlibrary.R;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallBeatIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallClipRotateIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallClipRotateMultipleIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallClipRotatePulseIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallGridBeatIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallGridPulseIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallPulseIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallPulseRiseIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallPulseSyncIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallRotateIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallScaleIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallScaleMultipleIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallScaleRippleIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallScaleRippleMultipleIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallSpinFadeLoaderIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallTrianglePathIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallZigZagDeflectIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BallZigZagIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.BaseIndicatorController;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.CubeTransitionIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.LineScaleIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.LineScalePartyIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.LineScalePulseOutIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.LineScalePulseOutRapidIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.LineSpinFadeLoaderIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.PacmanIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.SemiCircleSpinIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.SquareSpinIndicator;
import com.superrecycleview.superlibrary.recycleview.progressindicator.indicator.TriangleSkewSpinIndicator;
/**
* Created by Jack on 2015/10/19.
.BallPulse,
.BallGridPulse,
.BallClipRotate,
.BallClipRotatePulse,
.SquareSpin,
.BallClipRotateMultiple,
.BallPulseRise,
.BallRotate,
.CubeTransition,
.BallZigZag,
.BallZigZagDeflect,
.BallTrianglePath,
.BallScale,
.LineScale,
.LineScaleParty,
.BallScaleMultiple,
.BallPulseSync,
.BallBeat,
.LineScalePulseOut,
.LineScalePulseOutRapid,
.BallScaleRipple,
.BallScaleRippleMultiple,
.BallSpinFadeLoader,
.LineSpinFadeLoader,
.TriangleSkewSpin,
.Pacman,
.BallGridBeat,
.SemiCircleSpin
*
*/
public class AVLoadingIndicatorView extends View {
//indicators
public static final int BallPulse=0;
public static final int BallGridPulse=1;
public static final int BallClipRotate=2;
public static final int BallClipRotatePulse=3;
public static final int SquareSpin=4;
public static final int BallClipRotateMultiple=5;
public static final int BallPulseRise=6;
public static final int BallRotate=7;
public static final int CubeTransition=8;
public static final int BallZigZag=9;
public static final int BallZigZagDeflect=10;
public static final int BallTrianglePath=11;
public static final int BallScale=12;
public static final int LineScale=13;
public static final int LineScaleParty=14;
public static final int BallScaleMultiple=15;
public static final int BallPulseSync=16;
public static final int BallBeat=17;
public static final int LineScalePulseOut=18;
public static final int LineScalePulseOutRapid=19;
public static final int BallScaleRipple=20;
public static final int BallScaleRippleMultiple=21;
public static final int BallSpinFadeLoader=22;
public static final int LineSpinFadeLoader=23;
public static final int TriangleSkewSpin=24;
public static final int Pacman=25;
public static final int BallGridBeat=26;
public static final int SemiCircleSpin=27;
@IntDef(flag = true,
value = {
BallPulse,
BallGridPulse,
BallClipRotate,
BallClipRotatePulse,
SquareSpin,
BallClipRotateMultiple,
BallPulseRise,
BallRotate,
CubeTransition,
BallZigZag,
BallZigZagDeflect,
BallTrianglePath,
BallScale,
LineScale,
LineScaleParty,
BallScaleMultiple,
BallPulseSync,
BallBeat,
LineScalePulseOut,
LineScalePulseOutRapid,
BallScaleRipple,
BallScaleRippleMultiple,
BallSpinFadeLoader,
LineSpinFadeLoader,
TriangleSkewSpin,
Pacman,
BallGridBeat,
SemiCircleSpin
})
public @interface Indicator{}
//Sizes (with defaults in DP)
public static final int DEFAULT_SIZE=30;
//attrs
int mIndicatorId;
int mIndicatorColor;
Paint mPaint;
BaseIndicatorController mIndicatorController;
private boolean mHasAnimation;
public AVLoadingIndicatorView(Context context) {
super(context);
init(null, 0);
}
public AVLoadingIndicatorView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public AVLoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public AVLoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr);
}
private void init(AttributeSet attrs, int defStyle) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.AVLoadingIndicatorView);
mIndicatorId=a.getInt(R.styleable.AVLoadingIndicatorView_indicator, BallPulse);
mIndicatorColor=a.getColor(R.styleable.AVLoadingIndicatorView_indicator_color, Color.WHITE);
a.recycle();
mPaint=new Paint();
mPaint.setColor(mIndicatorColor);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true);
applyIndicator();
}
public void setIndicatorId(int indicatorId){
mIndicatorId = indicatorId;
applyIndicator();
}
public void setIndicatorColor(int color){
mIndicatorColor = color;
mPaint.setColor(mIndicatorColor);
this.invalidate();
}
private void applyIndicator(){
switch (mIndicatorId){
case BallPulse:
mIndicatorController=new BallPulseIndicator();
break;
case BallGridPulse:
mIndicatorController=new BallGridPulseIndicator();
break;
case BallClipRotate:
mIndicatorController=new BallClipRotateIndicator();
break;
case BallClipRotatePulse:
mIndicatorController=new BallClipRotatePulseIndicator();
break;
case SquareSpin:
mIndicatorController=new SquareSpinIndicator();
break;
case BallClipRotateMultiple:
mIndicatorController=new BallClipRotateMultipleIndicator();
break;
case BallPulseRise:
mIndicatorController=new BallPulseRiseIndicator();
break;
case BallRotate:
mIndicatorController=new BallRotateIndicator();
break;
case CubeTransition:
mIndicatorController=new CubeTransitionIndicator();
break;
case BallZigZag:
mIndicatorController=new BallZigZagIndicator();
break;
case BallZigZagDeflect:
mIndicatorController=new BallZigZagDeflectIndicator();
break;
case BallTrianglePath:
mIndicatorController=new BallTrianglePathIndicator();
break;
case BallScale:
mIndicatorController=new BallScaleIndicator();
break;
case LineScale:
mIndicatorController=new LineScaleIndicator();
break;
case LineScaleParty:
mIndicatorController=new LineScalePartyIndicator();
break;
case BallScaleMultiple:
mIndicatorController=new BallScaleMultipleIndicator();
break;
case BallPulseSync:
mIndicatorController=new BallPulseSyncIndicator();
break;
case BallBeat:
mIndicatorController=new BallBeatIndicator();
break;
case LineScalePulseOut:
mIndicatorController=new LineScalePulseOutIndicator();
break;
case LineScalePulseOutRapid:
mIndicatorController=new LineScalePulseOutRapidIndicator();
break;
case BallScaleRipple:
mIndicatorController=new BallScaleRippleIndicator();
break;
case BallScaleRippleMultiple:
mIndicatorController=new BallScaleRippleMultipleIndicator();
break;
case BallSpinFadeLoader:
mIndicatorController=new BallSpinFadeLoaderIndicator();
break;
case LineSpinFadeLoader:
mIndicatorController=new LineSpinFadeLoaderIndicator();
break;
case TriangleSkewSpin:
mIndicatorController=new TriangleSkewSpinIndicator();
break;
case Pacman:
mIndicatorController=new PacmanIndicator();
break;
case BallGridBeat:
mIndicatorController=new BallGridBeatIndicator();
break;
case SemiCircleSpin:
mIndicatorController=new SemiCircleSpinIndicator();
break;
}
mIndicatorController.setTarget(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureDimension(dp2px(DEFAULT_SIZE), widthMeasureSpec);
int height = measureDimension(dp2px(DEFAULT_SIZE), heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureDimension(int defaultSize,int measureSpec){
int result = defaultSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(defaultSize, specSize);
} else {
result = defaultSize;
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawIndicator(canvas);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!mHasAnimation){
mHasAnimation=true;
applyAnimation();
}
}
@Override
public void setVisibility(int v) {
if (getVisibility() != v) {
super.setVisibility(v);
if (v == GONE || v == INVISIBLE) {
mIndicatorController.setAnimationStatus(BaseIndicatorController.AnimStatus.END);
} else {
mIndicatorController.setAnimationStatus(BaseIndicatorController.AnimStatus.START);
}
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIndicatorController.setAnimationStatus(BaseIndicatorController.AnimStatus.CANCEL);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mIndicatorController.setAnimationStatus(BaseIndicatorController.AnimStatus.START);
}
void drawIndicator(Canvas canvas){
mIndicatorController.draw(canvas, mPaint);
}
void applyAnimation(){
mIndicatorController.initAnimation();
}
private int dp2px(int dpValue) {
return (int) getContext().getResources().getDisplayMetrics().density * dpValue;
}
}

View File

@ -0,0 +1,82 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallBeatIndicator extends BaseIndicatorController {
public static final float SCALE=1.0f;
public static final int ALPHA=255;
private float[] scaleFloats=new float[]{SCALE,
SCALE,
SCALE};
int[] alphas=new int[]{ALPHA,
ALPHA,
ALPHA,};
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
float radius=(getWidth()-circleSpacing*2)/6;
float x = getWidth()/ 2-(radius*2+circleSpacing);
float y=getHeight() / 2;
for (int i = 0; i < 3; i++) {
canvas.save();
float translateX=x+(radius*2)*i+circleSpacing*i;
canvas.translate(translateX, y);
canvas.scale(scaleFloats[i], scaleFloats[i]);
paint.setAlpha(alphas[i]);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
int[] delays=new int[]{350,0,350};
for (int i = 0; i < 3; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.75f,1);
scaleAnim.setDuration(700);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(255,51,255);
alphaAnim.setDuration(700);
alphaAnim.setRepeatCount(-1);
alphaAnim.setStartDelay(delays[i]);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alphas[index] = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
}
return animators;
}
}

View File

@ -0,0 +1,65 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallClipRotateIndicator extends BaseIndicatorController {
float scaleFloat=1,degrees;
@Override
public void draw(Canvas canvas, Paint paint) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
float circleSpacing=12;
float x = (getWidth()) / 2;
float y=(getHeight()) / 2;
canvas.translate(x, y);
canvas.scale(scaleFloat, scaleFloat);
canvas.rotate(degrees);
RectF rectF=new RectF(-x+circleSpacing,-y+circleSpacing,0+x-circleSpacing,0+y-circleSpacing);
canvas.drawArc(rectF, -45, 270, false, paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.6f,0.5f,1);
scaleAnim.setDuration(750);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloat = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator rotateAnim= ValueAnimator.ofFloat(0,180,360);
rotateAnim.setDuration(750);
rotateAnim.setRepeatCount(-1);
rotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degrees = (float) animation.getAnimatedValue();
postInvalidate();
}
});
rotateAnim.start();
animators.add(scaleAnim);
animators.add(rotateAnim);
return animators;
}
}

View File

@ -0,0 +1,85 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallClipRotateMultipleIndicator extends BaseIndicatorController{
float scaleFloat=1,degrees;
@Override
public void draw(Canvas canvas, Paint paint) {
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
float circleSpacing=12;
float x=getWidth()/2;
float y=getHeight()/2;
canvas.save();
canvas.translate(x, y);
canvas.scale(scaleFloat, scaleFloat);
canvas.rotate(degrees);
//draw two big arc
float[] bStartAngles=new float[]{135,-45};
for (int i = 0; i < 2; i++) {
RectF rectF=new RectF(-x+circleSpacing,-y+circleSpacing,x-circleSpacing,y-circleSpacing);
canvas.drawArc(rectF, bStartAngles[i], 90, false, paint);
}
canvas.restore();
canvas.translate(x, y);
canvas.scale(scaleFloat, scaleFloat);
canvas.rotate(-degrees);
//draw two small arc
float[] sStartAngles=new float[]{225,45};
for (int i = 0; i < 2; i++) {
RectF rectF=new RectF(-x/1.8f+circleSpacing,-y/1.8f+circleSpacing,x/1.8f-circleSpacing,y/1.8f-circleSpacing);
canvas.drawArc(rectF, sStartAngles[i], 90, false, paint);
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.6f,1);
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloat = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator rotateAnim= ValueAnimator.ofFloat(0, 180,360);
rotateAnim.setDuration(1000);
rotateAnim.setRepeatCount(-1);
rotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degrees = (float) animation.getAnimatedValue();
postInvalidate();
}
});
rotateAnim.start();
animators.add(scaleAnim);
animators.add(rotateAnim);
return animators;
}
}

View File

@ -0,0 +1,94 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallClipRotatePulseIndicator extends BaseIndicatorController {
float scaleFloat1,scaleFloat2,degrees;
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=12;
float x=getWidth()/2;
float y=getHeight()/2;
//draw fill circle
canvas.save();
canvas.translate(x, y);
canvas.scale(scaleFloat1, scaleFloat1);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(0, 0, x / 2.5f, paint);
canvas.restore();
canvas.translate(x, y);
canvas.scale(scaleFloat2, scaleFloat2);
canvas.rotate(degrees);
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
//draw two arc
float[] startAngles=new float[]{225,45};
for (int i = 0; i < 2; i++) {
RectF rectF=new RectF(-x+circleSpacing,-y+circleSpacing,x-circleSpacing,y-circleSpacing);
canvas.drawArc(rectF, startAngles[i], 90, false, paint);
}
}
@Override
public List<Animator> createAnimation() {
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.3f,1);
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloat1 = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator scaleAnim2= ValueAnimator.ofFloat(1,0.6f,1);
scaleAnim2.setDuration(1000);
scaleAnim2.setRepeatCount(-1);
scaleAnim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloat2 = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim2.start();
ValueAnimator rotateAnim= ValueAnimator.ofFloat(0, 180,360);
rotateAnim.setDuration(1000);
rotateAnim.setRepeatCount(-1);
rotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degrees = (float) animation.getAnimatedValue();
postInvalidate();
}
});
rotateAnim.start();
List<Animator> animators=new ArrayList<>();
animators.add(scaleAnim);
animators.add(scaleAnim2);
animators.add(rotateAnim);
return animators;
}
}

View File

@ -0,0 +1,76 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallGridBeatIndicator extends BaseIndicatorController {
public static final int ALPHA=255;
int[] alphas=new int[]{ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA};
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
float radius=(getWidth()-circleSpacing*4)/6;
float x = getWidth()/ 2-(radius*2+circleSpacing);
float y = getWidth()/ 2-(radius*2+circleSpacing);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
canvas.save();
float translateX=x+(radius*2)*j+circleSpacing*j;
float translateY=y+(radius*2)*i+circleSpacing*i;
canvas.translate(translateX, translateY);
paint.setAlpha(alphas[3 * i + j]);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
}
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
int[] durations={960, 930, 1190, 1130, 1340, 940, 1200, 820, 1190};
int[] delays= {360, 400, 680, 410, 710, -150, -120, 10, 320};
for (int i = 0; i < 9; i++) {
final int index=i;
ValueAnimator alphaAnim= ValueAnimator.ofInt(255, 168,255);
alphaAnim.setDuration(durations[i]);
alphaAnim.setRepeatCount(-1);
alphaAnim.setStartDelay(delays[i]);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alphas[index] = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
animators.add(alphaAnim);
}
return animators;
}
}

View File

@ -0,0 +1,103 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallGridPulseIndicator extends BaseIndicatorController{
public static final int ALPHA=255;
public static final float SCALE=1.0f;
int[] alphas=new int[]{ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA};
float[] scaleFloats=new float[]{SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE};
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
float radius=(getWidth()-circleSpacing*4)/6;
float x = getWidth()/ 2-(radius*2+circleSpacing);
float y = getWidth()/ 2-(radius*2+circleSpacing);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
canvas.save();
float translateX=x+(radius*2)*j+circleSpacing*j;
float translateY=y+(radius*2)*i+circleSpacing*i;
canvas.translate(translateX, translateY);
canvas.scale(scaleFloats[3 * i + j], scaleFloats[3 * i + j]);
paint.setAlpha(alphas[3 * i + j]);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
}
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
int[] durations={720, 1020, 1280, 1420, 1450, 1180, 870, 1450, 1060};
int[] delays= {-60, 250, -170, 480, 310, 30, 460, 780, 450};
for (int i = 0; i < 9; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.5f,1);
scaleAnim.setDuration(durations[i]);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(255, 210, 122, 255);
alphaAnim.setDuration(durations[i]);
alphaAnim.setRepeatCount(-1);
alphaAnim.setStartDelay(delays[i]);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alphas[index] = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
}
return animators;
}
}

View File

@ -0,0 +1,68 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallPulseIndicator extends BaseIndicatorController{
public static final float SCALE=1.0f;
//scale x ,y
private float[] scaleFloats=new float[]{SCALE,
SCALE,
SCALE};
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
float radius=(Math.min(getWidth(),getHeight())-circleSpacing*2)/6;
float x = getWidth()/ 2-(radius*2+circleSpacing);
float y=getHeight() / 2;
for (int i = 0; i < 3; i++) {
canvas.save();
float translateX=x+(radius*2)*i+circleSpacing*i;
canvas.translate(translateX, y);
canvas.scale(scaleFloats[i], scaleFloats[i]);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
int[] delays=new int[]{120,240,360};
for (int i = 0; i < 3; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.3f,1);
scaleAnim.setDuration(750);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
animators.add(scaleAnim);
}
return animators;
}
}

View File

@ -0,0 +1,41 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallPulseRiseIndicator extends BaseIndicatorController{
@Override
public void draw(Canvas canvas, Paint paint) {
float radius=getWidth()/10;
canvas.drawCircle(getWidth()/4,radius*2,radius,paint);
canvas.drawCircle(getWidth()*3/4,radius*2,radius,paint);
canvas.drawCircle(radius,getHeight()-2*radius,radius,paint);
canvas.drawCircle(getWidth()/2,getHeight()-2*radius,radius,paint);
canvas.drawCircle(getWidth()-radius,getHeight()-2*radius,radius,paint);
}
@Override
public List<Animator> createAnimation() {
PropertyValuesHolder rotation6= PropertyValuesHolder.ofFloat("rotationX",0,360);
ObjectAnimator animator= ObjectAnimator.ofPropertyValuesHolder(getTarget(), rotation6);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(-1);
animator.setDuration(1500);
animator.start();
List<Animator> animators=new ArrayList<>();
animators.add(animator);
return animators;
}
}

View File

@ -0,0 +1,57 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallPulseSyncIndicator extends BaseIndicatorController {
float[] translateYFloats=new float[3];
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
float radius=(getWidth()-circleSpacing*2)/6;
float x = getWidth()/ 2-(radius*2+circleSpacing);
for (int i = 0; i < 3; i++) {
canvas.save();
float translateX=x+(radius*2)*i+circleSpacing*i;
canvas.translate(translateX, translateYFloats[i]);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
float circleSpacing=4;
float radius=(getWidth()-circleSpacing*2)/6;
int[] delays=new int[]{70,140,210};
for (int i = 0; i < 3; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(getHeight()/2,getHeight()/2-radius*2,getHeight()/2);
scaleAnim.setDuration(600);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateYFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
animators.add(scaleAnim);
}
return animators;
}
}

View File

@ -0,0 +1,71 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallRotateIndicator extends BaseIndicatorController{
float scaleFloat=0.5f;
@Override
public void draw(Canvas canvas, Paint paint) {
float radius=getWidth()/10;
float x = getWidth()/ 2;
float y=getHeight()/2;
canvas.save();
canvas.translate(x - radius * 2 - radius, y);
canvas.scale(scaleFloat, scaleFloat);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
canvas.save();
canvas.translate(x, y);
canvas.scale(scaleFloat, scaleFloat);
canvas.drawCircle(0, 0, radius, paint);
canvas.restore();
canvas.save();
canvas.translate(x + radius * 2 + radius, y);
canvas.scale(scaleFloat, scaleFloat);
canvas.drawCircle(0,0,radius, paint);
canvas.restore();
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
ValueAnimator scaleAnim= ValueAnimator.ofFloat(0.5f,1,0.5f);
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloat = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ObjectAnimator rotateAnim= ObjectAnimator.ofFloat(getTarget(),"rotation",0,180,360);
rotateAnim.setDuration(1000);
rotateAnim.setRepeatCount(-1);
rotateAnim.start();
animators.add(scaleAnim);
animators.add(rotateAnim);
return animators;
}
}

View File

@ -0,0 +1,63 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallScaleIndicator extends BaseIndicatorController {
float scale=1;
int alpha=255;
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
paint.setAlpha(alpha);
canvas.scale(scale,scale,getWidth()/2,getHeight()/2);
paint.setAlpha(alpha);
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-circleSpacing,paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
ValueAnimator scaleAnim= ValueAnimator.ofFloat(0,1);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scale = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(255, 0);
alphaAnim.setInterpolator(new LinearInterpolator());
alphaAnim.setDuration(1000);
alphaAnim.setRepeatCount(-1);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alpha = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
return animators;
}
}

View File

@ -0,0 +1,70 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallScaleMultipleIndicator extends BaseIndicatorController {
float[] scaleFloats=new float[]{1,1,1};
int[] alphaInts=new int[]{255,255,255};
@Override
public void draw(Canvas canvas, Paint paint) {
float circleSpacing=4;
for (int i = 0; i < 3; i++) {
paint.setAlpha(alphaInts[i]);
canvas.scale(scaleFloats[i],scaleFloats[i],getWidth()/2,getHeight()/2);
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-circleSpacing,paint);
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
long[] delays=new long[]{0, 200, 400};
for (int i = 0; i < 3; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(0,1);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.setStartDelay(delays[i]);
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(255,0);
alphaAnim.setInterpolator(new LinearInterpolator());
alphaAnim.setDuration(1000);
alphaAnim.setRepeatCount(-1);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alphaInts[index] = (int) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.setStartDelay(delays[i]);
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
}
return animators;
}
}

View File

@ -0,0 +1,59 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallScaleRippleIndicator extends BallScaleIndicator {
@Override
public void draw(Canvas canvas, Paint paint) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
super.draw(canvas, paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
ValueAnimator scaleAnim= ValueAnimator.ofFloat(0,1);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scale = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(0, 255);
alphaAnim.setInterpolator(new LinearInterpolator());
alphaAnim.setDuration(1000);
alphaAnim.setRepeatCount(-1);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alpha = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
return animators;
}
}

View File

@ -0,0 +1,65 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallScaleRippleMultipleIndicator extends BallScaleMultipleIndicator{
@Override
public void draw(Canvas canvas, Paint paint) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
super.draw(canvas, paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
long[] delays=new long[]{0, 200, 400};
for (int i = 0; i < 3; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(0,1);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.setStartDelay(delays[i]);
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(0,255);
scaleAnim.setInterpolator(new LinearInterpolator());
alphaAnim.setDuration(1000);
alphaAnim.setRepeatCount(-1);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alphaInts[index] = (int) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.setStartDelay(delays[i]);
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
}
return animators;
}
}

View File

@ -0,0 +1,116 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallSpinFadeLoaderIndicator extends BaseIndicatorController {
public static final float SCALE=1.0f;
public static final int ALPHA=255;
float[] scaleFloats=new float[]{SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE,
SCALE};
int[] alphas=new int[]{ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA,
ALPHA};
@Override
public void draw(Canvas canvas, Paint paint) {
float radius=getWidth()/10;
for (int i = 0; i < 8; i++) {
canvas.save();
Point point=circleAt(getWidth(),getHeight(),getWidth()/2-radius,i*(Math.PI/4));
canvas.translate(point.x,point.y);
canvas.scale(scaleFloats[i],scaleFloats[i]);
paint.setAlpha(alphas[i]);
canvas.drawCircle(0,0,radius,paint);
canvas.restore();
}
}
/**
* 圆O的圆心为(a,b),半径为R,点A与到X轴的为角α.
*则点A的坐标为(a+R*cosα,b+R*sinα)
* @param width
* @param height
* @param radius
* @param angle
* @return
*/
Point circleAt(int width,int height,float radius,double angle){
float x= (float) (width/2+radius*(Math.cos(angle)));
float y= (float) (height/2+radius*(Math.sin(angle)));
return new Point(x,y);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
int[] delays= {0, 120, 240, 360, 480, 600, 720, 780, 840};
for (int i = 0; i < 8; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.4f,1);
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(255, 77, 255);
alphaAnim.setDuration(1000);
alphaAnim.setRepeatCount(-1);
alphaAnim.setStartDelay(delays[i]);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alphas[index] = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
animators.add(scaleAnim);
animators.add(alphaAnim);
}
return animators;
}
final class Point{
public float x;
public float y;
public Point(float x, float y){
this.x=x;
this.y=y;
}
}
}

View File

@ -0,0 +1,82 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallTrianglePathIndicator extends BaseIndicatorController {
float[] translateX=new float[3],translateY=new float[3];
@Override
public void draw(Canvas canvas, Paint paint) {
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
for (int i = 0; i < 3; i++) {
canvas.save();
canvas.translate(translateX[i], translateY[i]);
canvas.drawCircle(0, 0, getWidth() / 10, paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
float startX=getWidth()/5;
float startY=getWidth()/5;
for (int i = 0; i < 3; i++) {
final int index=i;
ValueAnimator translateXAnim= ValueAnimator.ofFloat(getWidth()/2,getWidth()-startX,startX,getWidth()/2);
if (i==1){
translateXAnim= ValueAnimator.ofFloat(getWidth()-startX,startX,getWidth()/2,getWidth()-startX);
}else if (i==2){
translateXAnim= ValueAnimator.ofFloat(startX,getWidth()/2,getWidth()-startX,startX);
}
ValueAnimator translateYAnim= ValueAnimator.ofFloat(startY,getHeight()-startY,getHeight()-startY,startY);
if (i==1){
translateYAnim= ValueAnimator.ofFloat(getHeight()-startY,getHeight()-startY,startY,getHeight()-startY);
}else if (i==2){
translateYAnim= ValueAnimator.ofFloat(getHeight()-startY,startY,getHeight()-startY,getHeight()-startY);
}
translateXAnim.setDuration(2000);
translateXAnim.setInterpolator(new LinearInterpolator());
translateXAnim.setRepeatCount(-1);
translateXAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateX [index]= (float) animation.getAnimatedValue();
postInvalidate();
}
});
translateXAnim.start();
translateYAnim.setDuration(2000);
translateYAnim.setInterpolator(new LinearInterpolator());
translateYAnim.setRepeatCount(-1);
translateYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateY [index]= (float) animation.getAnimatedValue();
postInvalidate();
}
});
translateYAnim.start();
animators.add(translateXAnim);
animators.add(translateYAnim);
}
return animators;
}
}

View File

@ -0,0 +1,65 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallZigZagDeflectIndicator extends BallZigZagIndicator {
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
float startX=getWidth()/6;
float startY=getWidth()/6;
for (int i = 0; i < 2; i++) {
final int index=i;
ValueAnimator translateXAnim= ValueAnimator.ofFloat(startX,getWidth()-startX,startX,getWidth()-startX,startX);
if (i==1){
translateXAnim= ValueAnimator.ofFloat(getWidth()-startX,startX,getWidth()-startX,startX,getWidth()-startX);
}
ValueAnimator translateYAnim= ValueAnimator.ofFloat(startY,startY,getHeight()-startY,getHeight()-startY,startY);
if (i==1){
translateYAnim= ValueAnimator.ofFloat(getHeight()-startY,getHeight()-startY,startY,startY,getHeight()-startY);
}
translateXAnim.setDuration(2000);
translateXAnim.setInterpolator(new LinearInterpolator());
translateXAnim.setRepeatCount(-1);
translateXAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateX [index]= (float) animation.getAnimatedValue();
postInvalidate();
}
});
translateXAnim.start();
translateYAnim.setDuration(2000);
translateYAnim.setInterpolator(new LinearInterpolator());
translateYAnim.setRepeatCount(-1);
translateYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateY [index]= (float) animation.getAnimatedValue();
postInvalidate();
}
});
translateYAnim.start();
animators.add(translateXAnim);
animators.add(translateYAnim);
}
return animators;
}
}

View File

@ -0,0 +1,75 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class BallZigZagIndicator extends BaseIndicatorController {
float[] translateX=new float[2],translateY=new float[2];
@Override
public void draw(Canvas canvas, Paint paint) {
for (int i = 0; i < 2; i++) {
canvas.save();
canvas.translate(translateX[i], translateY[i]);
canvas.drawCircle(0, 0, getWidth() / 10, paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
float startX=getWidth()/6;
float startY=getWidth()/6;
for (int i = 0; i < 2; i++) {
final int index=i;
ValueAnimator translateXAnim= ValueAnimator.ofFloat(startX,getWidth()-startX,getWidth()/2,startX);
if (i==1){
translateXAnim= ValueAnimator.ofFloat(getWidth()-startX,startX,getWidth()/2,getWidth()-startX);
}
ValueAnimator translateYAnim= ValueAnimator.ofFloat(startY,startY,getHeight()/2,startY);
if (i==1){
translateYAnim= ValueAnimator.ofFloat(getHeight()-startY,getHeight()-startY,getHeight()/2,getHeight()-startY);
}
translateXAnim.setDuration(1000);
translateXAnim.setInterpolator(new LinearInterpolator());
translateXAnim.setRepeatCount(-1);
translateXAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateX[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
translateXAnim.start();
translateYAnim.setDuration(1000);
translateYAnim.setInterpolator(new LinearInterpolator());
translateYAnim.setRepeatCount(-1);
translateYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateY[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
translateYAnim.start();
animators.add(translateXAnim);
animators.add(translateYAnim);
}
return animators;
}
}

View File

@ -0,0 +1,100 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public abstract class BaseIndicatorController {
private View mTarget;
private List<Animator> mAnimators;
public void setTarget(View target){
this.mTarget=target;
}
public View getTarget(){
return mTarget;
}
public int getWidth(){
return mTarget.getWidth();
}
public int getHeight(){
return mTarget.getHeight();
}
public void postInvalidate(){
mTarget.postInvalidate();
}
/**
* draw indicator
* @param canvas
* @param paint
*/
public abstract void draw(Canvas canvas, Paint paint);
/**
* create animation or animations
*/
public abstract List<Animator> createAnimation();
public void initAnimation(){
mAnimators=createAnimation();
}
/**
* make animation to start or end when target
* view was be Visible or Gone or Invisible.
* make animation to cancel when target view
* be onDetachedFromWindow.
* @param animStatus
*/
public void setAnimationStatus(AnimStatus animStatus){
if (mAnimators==null){
return;
}
int count=mAnimators.size();
for (int i = 0; i < count; i++) {
Animator animator=mAnimators.get(i);
boolean isRunning=animator.isRunning();
switch (animStatus){
case START:
if (!isRunning){
animator.start();
}
break;
case END:
if (isRunning){
animator.end();
}
break;
case CANCEL:
if (isRunning){
animator.cancel();
}
break;
}
}
}
public enum AnimStatus{
START,END,CANCEL
}
}

View File

@ -0,0 +1,110 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class CubeTransitionIndicator extends BaseIndicatorController {
float[] translateX=new float[2],translateY=new float[2];
float degrees,scaleFloat=1.0f;
@Override
public void draw(Canvas canvas, Paint paint) {
float rWidth=getWidth()/5;
float rHeight=getHeight()/5;
for (int i = 0; i < 2; i++) {
canvas.save();
canvas.translate(translateX[i], translateY[i]);
canvas.rotate(degrees);
canvas.scale(scaleFloat,scaleFloat);
RectF rectF=new RectF(-rWidth/2,-rHeight/2,rWidth/2,rHeight/2);
canvas.drawRect(rectF,paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
float startX=getWidth()/5;
float startY=getHeight()/5;
for (int i = 0; i < 2; i++) {
final int index=i;
translateX[index]=startX;
ValueAnimator translationXAnim= ValueAnimator.ofFloat(startX,getWidth()-startX,getWidth()-startX, startX,startX);
if (i==1){
translationXAnim= ValueAnimator.ofFloat(getWidth()-startX,startX,startX, getWidth()-startX,getWidth()-startX);
}
translationXAnim.setInterpolator(new LinearInterpolator());
translationXAnim.setDuration(1600);
translationXAnim.setRepeatCount(-1);
translationXAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateX[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
translationXAnim.start();
translateY[index]=startY;
ValueAnimator translationYAnim= ValueAnimator.ofFloat(startY,startY,getHeight()-startY,getHeight()- startY,startY);
if (i==1){
translationYAnim= ValueAnimator.ofFloat(getHeight()-startY,getHeight()-startY,startY,startY,getHeight()-startY);
}
translationYAnim.setDuration(1600);
translationYAnim.setInterpolator(new LinearInterpolator());
translationYAnim.setRepeatCount(-1);
translationYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateY[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
translationYAnim.start();
animators.add(translationXAnim);
animators.add(translationYAnim);
}
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.5f,1,0.5f,1);
scaleAnim.setDuration(1600);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloat = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
ValueAnimator rotateAnim= ValueAnimator.ofFloat(0,180,360,1.5f*360,2*360);
rotateAnim.setDuration(1600);
rotateAnim.setInterpolator(new LinearInterpolator());
rotateAnim.setRepeatCount(-1);
rotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degrees = (float) animation.getAnimatedValue();
postInvalidate();
}
});
rotateAnim.start();
animators.add(scaleAnim);
animators.add(rotateAnim);
return animators;
}
}

View File

@ -0,0 +1,62 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class LineScaleIndicator extends BaseIndicatorController {
public static final float SCALE=1.0f;
float[] scaleYFloats=new float[]{SCALE,
SCALE,
SCALE,
SCALE,
SCALE,};
@Override
public void draw(Canvas canvas, Paint paint) {
float translateX=getWidth()/11;
float translateY=getHeight()/2;
for (int i = 0; i < 5; i++) {
canvas.save();
canvas.translate((2 + i * 2) * translateX - translateX / 2, translateY);
canvas.scale(SCALE, scaleYFloats[i]);
RectF rectF=new RectF(-translateX/2,-getHeight()/2.5f,translateX/2,getHeight()/2.5f);
canvas.drawRoundRect(rectF, 5, 5, paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
long[] delays=new long[]{100,200,300,400,500};
for (int i = 0; i < 5; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1, 0.4f, 1);
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleYFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
animators.add(scaleAnim);
}
return animators;
}
}

View File

@ -0,0 +1,64 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class LineScalePartyIndicator extends BaseIndicatorController {
public static final float SCALE=1.0f;
float[] scaleFloats=new float[]{SCALE,
SCALE,
SCALE,
SCALE,
SCALE,};
@Override
public void draw(Canvas canvas, Paint paint) {
float translateX=getWidth()/9;
float translateY=getHeight()/2;
for (int i = 0; i < 4; i++) {
canvas.save();
canvas.translate((2 + i * 2) * translateX - translateX / 2, translateY);
canvas.scale(scaleFloats[i], scaleFloats[i]);
RectF rectF=new RectF(-translateX/2,-getHeight()/2.5f,translateX/2,getHeight()/2.5f);
canvas.drawRoundRect(rectF,5,5,paint);
canvas.restore();
}
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
long[] durations=new long[]{1260, 430, 1010, 730};
long[] delays=new long[]{770, 290, 280, 740};
for (int i = 0; i < 4; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.4f,1);
scaleAnim.setDuration(durations[i]);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
animators.add(scaleAnim);
}
return animators;
}
}

View File

@ -0,0 +1,37 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class LineScalePulseOutIndicator extends LineScaleIndicator {
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
long[] delays=new long[]{500,250,0,250,500};
for (int i = 0; i < 5; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.3f,1);
scaleAnim.setDuration(900);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleYFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
animators.add(scaleAnim);
}
return animators;
}
}

View File

@ -0,0 +1,37 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class LineScalePulseOutRapidIndicator extends LineScaleIndicator {
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
long[] delays=new long[]{400,200,0,200,400};
for (int i = 0; i < 5; i++) {
final int index=i;
ValueAnimator scaleAnim= ValueAnimator.ofFloat(1,0.4f,1);
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.setStartDelay(delays[i]);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleYFloats[index] = (float) animation.getAnimatedValue();
postInvalidate();
}
});
scaleAnim.start();
animators.add(scaleAnim);
}
return animators;
}
}

View File

@ -0,0 +1,29 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
/**
* Created by Jack on 2015/10/19.
*/
public class LineSpinFadeLoaderIndicator extends BallSpinFadeLoaderIndicator {
@Override
public void draw(Canvas canvas, Paint paint) {
float radius=getWidth()/10;
for (int i = 0; i < 8; i++) {
canvas.save();
Point point=circleAt(getWidth(),getHeight(),getWidth()/2.5f-radius,i*(Math.PI/4));
canvas.translate(point.x, point.y);
canvas.scale(scaleFloats[i], scaleFloats[i]);
canvas.rotate(i*45);
paint.setAlpha(alphas[i]);
RectF rectF=new RectF(-radius,-radius/1.5f,1.5f*radius,radius/1.5f);
canvas.drawRoundRect(rectF,5,5,paint);
canvas.restore();
}
}
}

View File

@ -0,0 +1,119 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class PacmanIndicator extends BaseIndicatorController{
private float translateX;
private int alpha;
private float degrees1,degrees2;
@Override
public void draw(Canvas canvas, Paint paint) {
drawPacman(canvas,paint);
drawCircle(canvas,paint);
}
private void drawPacman(Canvas canvas, Paint paint){
float x=getWidth()/2;
float y=getHeight()/2;
canvas.save();
canvas.translate(x, y);
canvas.rotate(degrees1);
paint.setAlpha(255);
RectF rectF1=new RectF(-x/1.7f,-y/1.7f,x/1.7f,y/1.7f);
canvas.drawArc(rectF1, 0, 270, true, paint);
canvas.restore();
canvas.save();
canvas.translate(x, y);
canvas.rotate(degrees2);
paint.setAlpha(255);
RectF rectF2=new RectF(-x/1.7f,-y/1.7f,x/1.7f,y/1.7f);
canvas.drawArc(rectF2,90,270,true,paint);
canvas.restore();
}
private void drawCircle(Canvas canvas, Paint paint) {
float radius=getWidth()/11;
paint.setAlpha(alpha);
canvas.drawCircle(translateX, getHeight() / 2, radius, paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
float startT=getWidth()/11;
ValueAnimator translationAnim= ValueAnimator.ofFloat(getWidth()-startT,getWidth()/2);
translationAnim.setDuration(650);
translationAnim.setInterpolator(new LinearInterpolator());
translationAnim.setRepeatCount(-1);
translationAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translateX = (float) animation.getAnimatedValue();
postInvalidate();
}
});
translationAnim.start();
ValueAnimator alphaAnim= ValueAnimator.ofInt(255,122);
alphaAnim.setDuration(650);
alphaAnim.setRepeatCount(-1);
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
alpha = (int) animation.getAnimatedValue();
postInvalidate();
}
});
alphaAnim.start();
ValueAnimator rotateAnim1= ValueAnimator.ofFloat(0, 45, 0);
rotateAnim1.setDuration(650);
rotateAnim1.setRepeatCount(-1);
rotateAnim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degrees1 = (float) animation.getAnimatedValue();
postInvalidate();
}
});
rotateAnim1.start();
ValueAnimator rotateAnim2= ValueAnimator.ofFloat(0,-45,0);
rotateAnim2.setDuration(650);
rotateAnim2.setRepeatCount(-1);
rotateAnim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degrees2 = (float) animation.getAnimatedValue();
postInvalidate();
}
});
rotateAnim2.start();
animators.add(translationAnim);
animators.add(alphaAnim);
animators.add(rotateAnim1);
animators.add(rotateAnim2);
return animators;
}
}

View File

@ -0,0 +1,36 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class SemiCircleSpinIndicator extends BaseIndicatorController {
@Override
public void draw(Canvas canvas, Paint paint) {
RectF rectF=new RectF(0,0,getWidth(),getHeight());
canvas.drawArc(rectF,-60,120,false,paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
ObjectAnimator rotateAnim= ObjectAnimator.ofFloat(getTarget(),"rotation",0,180,360);
rotateAnim.setDuration(600);
rotateAnim.setRepeatCount(-1);
rotateAnim.start();
animators.add(rotateAnim);
return animators;
}
}

View File

@ -0,0 +1,37 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class SquareSpinIndicator extends BaseIndicatorController {
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.drawRect(new RectF(getWidth()/5,getHeight()/5,getWidth()*4/5,getHeight()*4/5),paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
PropertyValuesHolder rotation5= PropertyValuesHolder.ofFloat("rotationX",0,180,180,0,0);
PropertyValuesHolder rotation6= PropertyValuesHolder.ofFloat("rotationY",0,0,180,180,0);
ObjectAnimator animator= ObjectAnimator.ofPropertyValuesHolder(getTarget(), rotation6,rotation5);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(-1);
animator.setDuration(2500);
animator.start();
animators.add(animator);
return animators;
}
}

View File

@ -0,0 +1,45 @@
package com.superrecycleview.superlibrary.recycleview.progressindicator.indicator;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jack on 2015/10/19.
*/
public class TriangleSkewSpinIndicator extends BaseIndicatorController {
@Override
public void draw(Canvas canvas, Paint paint) {
Path path=new Path();
path.moveTo(getWidth()/5,getHeight()*4/5);
path.lineTo(getWidth()*4/5, getHeight()*4/5);
path.lineTo(getWidth()/2,getHeight()/5);
path.close();
canvas.drawPath(path, paint);
}
@Override
public List<Animator> createAnimation() {
List<Animator> animators=new ArrayList<>();
PropertyValuesHolder rotation5= PropertyValuesHolder.ofFloat("rotationX",0,180,180,0,0);
PropertyValuesHolder rotation6= PropertyValuesHolder.ofFloat("rotationY",0,0,180,180,0);
ObjectAnimator animator= ObjectAnimator.ofPropertyValuesHolder(getTarget(), rotation6,rotation5);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(-1);
animator.setDuration(2500);
animator.start();
animators.add(animator);
return animators;
}
}

View File

@ -0,0 +1,275 @@
package com.superrecycleview.superlibrary.recycleview.swipemenu;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import androidx.core.view.GestureDetectorCompat;
import androidx.core.widget.ScrollerCompat;
import android.util.AttributeSet;
import android.view.GestureDetector.OnGestureListener;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.superrecycleview.superlibrary.R;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public class SuperSwipeMenuLayout extends FrameLayout {
private static final int STATE_CLOSE = 0;
private static final int STATE_OPEN = 1;
private static final boolean OVER_API_11 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
private int mSwipeDirection;
private View mContentView;
private View mMenuView;
private int mDownX;
private int state = STATE_CLOSE;
private GestureDetectorCompat mGestureDetector;
private OnGestureListener mGestureListener;
private boolean isFling;
private ScrollerCompat mOpenScroller;
private ScrollerCompat mCloseScroller;
private int mBaseX;
private Interpolator mCloseInterpolator;
private Interpolator mOpenInterpolator;
private ViewConfiguration mViewConfiguration;
private boolean swipeEnable = true;
private int animDuration;
public SuperSwipeMenuLayout(Context context) {
this(context, null);
}
public SuperSwipeMenuLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SuperSwipeMenuLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if(!isInEditMode()){
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeMenu, 0, defStyle);
animDuration = a.getInteger(R.styleable.SwipeMenu_anim_duration, 500);
a.recycle();
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
setClickable(true);
mContentView = findViewById(R.id.smContentView);
if (mContentView == null) {
throw new IllegalArgumentException("not find contentView by id smContentView");
}
mMenuView = findViewById(R.id.smMenuView);
if (mMenuView == null) {
throw new IllegalArgumentException("not find menuView by id smMenuView");
}
mViewConfiguration = ViewConfiguration.get(getContext());
init();
}
public void setSwipeDirection(int swipeDirection) {
mSwipeDirection = swipeDirection;
}
public void init() {
mGestureListener = new SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
isFling = false;
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
if (velocityX > mViewConfiguration.getScaledMinimumFlingVelocity() || velocityY > mViewConfiguration.getScaledMinimumFlingVelocity())
isFling = true;
return isFling;
}
};
mGestureDetector = new GestureDetectorCompat(getContext(),
mGestureListener);
mCloseScroller = ScrollerCompat.create(getContext());
mOpenScroller = ScrollerCompat.create(getContext());
}
public void setCloseInterpolator(Interpolator closeInterpolator) {
mCloseInterpolator = closeInterpolator;
if (mCloseInterpolator != null) {
mCloseScroller = ScrollerCompat.create(getContext(),
mCloseInterpolator);
}
}
public void setOpenInterpolator(Interpolator openInterpolator) {
mOpenInterpolator = openInterpolator;
if (mOpenInterpolator != null) {
mOpenScroller = ScrollerCompat.create(getContext(),
mOpenInterpolator);
}
}
public boolean onSwipe(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = (int) event.getX();
isFling = false;
break;
case MotionEvent.ACTION_MOVE:
int dis = (int) (mDownX - event.getX());
if (state == STATE_OPEN) {
dis += mMenuView.getWidth() * mSwipeDirection;
}
swipe(dis);
break;
case MotionEvent.ACTION_UP:
if ((isFling || Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 3)) &&
Math.signum(mDownX - event.getX()) == mSwipeDirection) {
smoothOpenMenu();
} else {
smoothCloseMenu();
return false;
}
break;
}
return true;
}
public boolean isOpen() {
return state == STATE_OPEN;
}
private void swipe(int dis) {
if (Math.signum(dis) != mSwipeDirection) {
dis = 0;
} else if (Math.abs(dis) > mMenuView.getWidth()) {
dis = mMenuView.getWidth() * mSwipeDirection;
state = STATE_OPEN;
}
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
int lGap = getPaddingLeft() + lp.leftMargin;
mContentView.layout(lGap - dis,
mContentView.getTop(),
lGap + (OVER_API_11 ? mContentView.getMeasuredWidthAndState() : mContentView.getMeasuredWidth()) - dis,
mContentView.getBottom());
if (mSwipeDirection == SuperSwipeMenuRecyclerView.DIRECTION_LEFT) {
mMenuView.layout(getMeasuredWidth() - dis, mMenuView.getTop(),
getMeasuredWidth() + (OVER_API_11 ? mMenuView.getMeasuredWidthAndState() : mMenuView.getMeasuredWidth()) - dis,
mMenuView.getBottom());
} else {
mMenuView.layout(-(OVER_API_11 ? mMenuView.getMeasuredWidthAndState() : mMenuView.getMeasuredWidth()) - dis, mMenuView.getTop(),
-dis, mMenuView.getBottom());
}
}
@Override
public void computeScroll() {
if (state == STATE_OPEN) {
if (mOpenScroller.computeScrollOffset()) {
swipe(mOpenScroller.getCurrX() * mSwipeDirection);
postInvalidate();
}
} else {
if (mCloseScroller.computeScrollOffset()) {
swipe((mBaseX - mCloseScroller.getCurrX()) * mSwipeDirection);
postInvalidate();
}
}
}
public void smoothCloseMenu() {
closeOpenedMenu();
}
public void closeOpenedMenu() {
state = STATE_CLOSE;
if (mSwipeDirection == SuperSwipeMenuRecyclerView.DIRECTION_LEFT) {
mBaseX = -mContentView.getLeft();
mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, animDuration);
} else {
mBaseX = mMenuView.getRight();
mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, animDuration);
}
postInvalidate();
}
public void smoothOpenMenu() {
state = STATE_OPEN;
if (mSwipeDirection == SuperSwipeMenuRecyclerView.DIRECTION_LEFT) {
mOpenScroller.startScroll(-mContentView.getLeft(), 0, mMenuView.getWidth(), 0, animDuration);
} else {
mOpenScroller.startScroll(mContentView.getLeft(), 0, mMenuView.getWidth(), 0, animDuration);
}
postInvalidate();
}
public void closeMenu() {
if (mCloseScroller.computeScrollOffset()) {
mCloseScroller.abortAnimation();
}
if (state == STATE_OPEN) {
state = STATE_CLOSE;
swipe(0);
}
}
public void openMenu() {
if (state == STATE_CLOSE) {
state = STATE_OPEN;
swipe(mMenuView.getWidth() * mSwipeDirection);
}
}
public View getMenuView() {
return mMenuView;
}
public View getContentView() {
return mContentView;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
FrameLayout.LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
int lGap = getPaddingLeft() + lp.leftMargin;
int tGap = getPaddingTop() + lp.topMargin;
mContentView.layout(lGap,
tGap,
lGap + (OVER_API_11 ? mContentView.getMeasuredWidthAndState() : mContentView.getMeasuredWidth()),
tGap + (OVER_API_11 ? mContentView.getMeasuredHeightAndState() : mContentView.getMeasuredHeight()));
lp = (LayoutParams) mMenuView.getLayoutParams();
tGap = getPaddingTop() + lp.topMargin;
if (mSwipeDirection == SuperSwipeMenuRecyclerView.DIRECTION_LEFT) {
mMenuView.layout(getMeasuredWidth(), tGap,
getMeasuredWidth() + (OVER_API_11 ? mMenuView.getMeasuredWidthAndState() : mMenuView.getMeasuredWidth()),
tGap + mMenuView.getMeasuredHeightAndState());
} else {
mMenuView.layout(-(OVER_API_11 ? mMenuView.getMeasuredWidthAndState() : mMenuView.getMeasuredWidth()), tGap,
0, tGap + mMenuView.getMeasuredHeightAndState());
}
}
public void setSwipeEnable(boolean swipeEnable) {
this.swipeEnable = swipeEnable;
}
public boolean isSwipeEnable() {
return swipeEnable;
}
}

View File

@ -0,0 +1,257 @@
package com.superrecycleview.superlibrary.recycleview.swipemenu;
import android.content.Context;
import androidx.core.view.MotionEventCompat;
import androidx.core.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import com.superrecycleview.superlibrary.recycleview.SuperRecyclerView;
/**
* Created by super南仔 on 07/28/16.
* blog: http://supercwn.github.io/
* GitHub: https://github.com/supercwn
*/
public class SuperSwipeMenuRecyclerView extends SuperRecyclerView {
public static final int TOUCH_STATE_NONE = 0;
public static final int TOUCH_STATE_X = 1;
public static final int TOUCH_STATE_Y = 2;
public static final int DIRECTION_LEFT = 1;
public static final int DIRECTION_RIGHT = -1;
protected int mDirection = DIRECTION_LEFT; // swipe from right to left by default
protected float mDownX;
protected float mDownY;
protected int mTouchState;
protected int mTouchPosition;
protected SuperSwipeMenuLayout mTouchView;
protected OnSwipeListener mOnSwipeListener;
protected Interpolator mCloseInterpolator;
protected Interpolator mOpenInterpolator;
protected LayoutManager mLlm;
protected ViewConfiguration mViewConfiguration;
protected long startClickTime;
protected float dx;
protected float dy;
public SuperSwipeMenuRecyclerView(Context context) {
this(context, null);
}
public SuperSwipeMenuRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SuperSwipeMenuRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
protected void init() {
mTouchState = TOUCH_STATE_NONE;
mViewConfiguration = ViewConfiguration.get(getContext());
}
public void setCloseInterpolator(Interpolator interpolator) {
mCloseInterpolator = interpolator;
}
public void setOpenInterpolator(Interpolator interpolator) {
mOpenInterpolator = interpolator;
}
public Interpolator getOpenInterpolator() {
return mOpenInterpolator;
}
public Interpolator getCloseInterpolator() {
return mCloseInterpolator;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null){
return super.onInterceptTouchEvent(ev);
}
int action = ev.getAction();
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
dx = 0.0f; // reset
dy = 0.0f; // reset
startClickTime = System.currentTimeMillis(); // reset
int oldPos = mTouchPosition;
mDownX = ev.getX();
mDownY = ev.getY();
mTouchState = TOUCH_STATE_NONE;
mTouchPosition = getChildAdapterPosition(findChildViewUnder((int) ev.getX(), (int) ev.getY()));
if (mTouchPosition == oldPos && mTouchView != null
&& mTouchView.isOpen()) {
mTouchState = TOUCH_STATE_X;
mTouchView.onSwipe(ev);
}
// find the touched child view
View view = null;
ViewHolder vh = findViewHolderForAdapterPosition(mTouchPosition);
if(vh != null){
view = vh.itemView;
}
// is not touched the opened menu view, so we intercept this touch event
if (mTouchPosition != oldPos && mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
mTouchView = null;
// try to cancel the touch event
MotionEvent cancelEvent = MotionEvent.obtain(ev);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(cancelEvent);
return true;
}
if (view instanceof SuperSwipeMenuLayout) {
mTouchView = (SuperSwipeMenuLayout) view;
mTouchView.setSwipeDirection(mDirection);
}
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
break;
case MotionEvent.ACTION_MOVE:
dy = Math.abs((ev.getY() - mDownY));
dx = Math.abs((ev.getX() - mDownX));
if (mTouchState == TOUCH_STATE_X && mTouchView.isSwipeEnable()) {
mTouchView.onSwipe(ev);
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
} else if (mTouchState == TOUCH_STATE_NONE && mTouchView.isSwipeEnable()) {
if (Math.abs(dy) > mViewConfiguration.getScaledTouchSlop()) {
mTouchState = TOUCH_STATE_Y;
} else if (dx > mViewConfiguration.getScaledTouchSlop()) {
mTouchState = TOUCH_STATE_X;
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeStart(mTouchPosition);
}
}
}
break;
case MotionEvent.ACTION_UP:
boolean isCloseOnUpEvent = false;
if (mTouchState == TOUCH_STATE_X && mTouchView.isSwipeEnable()) {
isCloseOnUpEvent = !mTouchView.onSwipe(ev);
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeEnd(mTouchPosition);
}
if (!mTouchView.isOpen()) {
mTouchPosition = -1;
mTouchView = null;
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
}
long clickDuration = System.currentTimeMillis() - startClickTime;
boolean isOutDuration = clickDuration > ViewConfiguration.getLongPressTimeout();
boolean isOutX = dx > mViewConfiguration.getScaledTouchSlop();
boolean isOutY = dy > mViewConfiguration.getScaledTouchSlop();
// long pressed or scaled touch, we just intercept up touch event
if(isOutDuration || isOutX || isOutY){
return true;
}else{
float eX = ev.getX();
float eY = ev.getY();
View upView = findChildViewUnder(eX, eY);
if(upView instanceof SuperSwipeMenuLayout){
SuperSwipeMenuLayout smView = (SuperSwipeMenuLayout)upView;
int x = (int)eX - smView.getLeft();
int y = (int)eY - smView.getTop();
View menuView = smView.getMenuView();
final float translationX = ViewCompat.getTranslationX(menuView);
final float translationY = ViewCompat.getTranslationY(menuView);
// intercept the up event when touched on the contentView of the opened SwipeMenuLayout
if (!(x >= menuView.getLeft() + translationX &&
x <= menuView.getRight() + translationX &&
y >= menuView.getTop() + translationY &&
y <= menuView.getBottom() + translationY) &&
isCloseOnUpEvent) {
return true;
}
}
}
break;
case MotionEvent.ACTION_CANCEL:
if(mTouchView != null && mTouchView.isSwipeEnable()){
// when event has canceled, we just consider as up event
ev.setAction(MotionEvent.ACTION_UP);
mTouchView.onSwipe(ev);
}
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* open menu manually
* @param position the adapter position
*/
public void smoothOpenMenu(int position) {
View view = mLlm.findViewByPosition(position);
if (view instanceof SuperSwipeMenuLayout) {
mTouchPosition = position;
// close pre opened swipe menu
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
}
mTouchView = (SuperSwipeMenuLayout) view;
mTouchView.setSwipeDirection(mDirection);
mTouchView.smoothOpenMenu();
}
}
/**
* close the opened menu manually
*/
public void smoothCloseMenu() {
// close the opened swipe menu
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
}
}
public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
this.mOnSwipeListener = onSwipeListener;
}
/**
* get current touched view
* @return touched view, maybe null
*/
public SuperSwipeMenuLayout getTouchView() {
return mTouchView;
}
public interface OnSwipeListener {
void onSwipeStart(int position);
void onSwipeEnd(int position);
}
/**
* set the swipe direction
* @param direction swipe direction (left or right)
*/
public void setSwipeDirection(int direction) {
mDirection = direction;
}
@Override
public void setLayoutManager(LayoutManager layout) {
super.setLayoutManager(layout);
mLlm = layout;
}
}

View File

@ -0,0 +1,266 @@
package com.superrecycleview.superlibrary.recycleview.swipemenu;
import android.content.Context;
import androidx.core.view.MotionEventCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
/**
* Created by super南仔 on 07/28/16. blog: http://supercwn.github.io/ GitHub:
* https://github.com/supercwn 该类用于slide不能使用SuperRecycle的侧滑
*/
public class SwipeMenuRecyclerView extends RecyclerView {
public static final int TOUCH_STATE_NONE = 0;
public static final int TOUCH_STATE_X = 1;
public static final int TOUCH_STATE_Y = 2;
public static final int DIRECTION_LEFT = 1;
public static final int DIRECTION_RIGHT = -1;
protected int mDirection = DIRECTION_LEFT; // swipe from right to left by
// default
protected float mDownX;
protected float mDownY;
protected int mTouchState;
protected int mTouchPosition;
protected SuperSwipeMenuLayout mTouchView;
protected OnSwipeListener mOnSwipeListener;
protected Interpolator mCloseInterpolator;
protected Interpolator mOpenInterpolator;
protected LayoutManager mLlm;
protected ViewConfiguration mViewConfiguration;
protected long startClickTime;
protected float dx;
protected float dy;
public SwipeMenuRecyclerView(Context context) {
this(context, null);
}
public SwipeMenuRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeMenuRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
protected void init() {
mTouchState = TOUCH_STATE_NONE;
mViewConfiguration = ViewConfiguration.get(getContext());
}
public void setCloseInterpolator(Interpolator interpolator) {
mCloseInterpolator = interpolator;
}
public void setOpenInterpolator(Interpolator interpolator) {
mOpenInterpolator = interpolator;
}
public Interpolator getOpenInterpolator() {
return mOpenInterpolator;
}
public Interpolator getCloseInterpolator() {
return mCloseInterpolator;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null) {
return super.onInterceptTouchEvent(ev);
}
int action = ev.getAction();
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
dx = 0.0f; // reset
dy = 0.0f; // reset
startClickTime = System.currentTimeMillis(); // reset
int oldPos = mTouchPosition;
mDownX = ev.getX();
mDownY = ev.getY();
mTouchState = TOUCH_STATE_NONE;
mTouchPosition = getChildAdapterPosition(
findChildViewUnder((int) ev.getX(), (int) ev.getY()));
if (mTouchPosition == oldPos && mTouchView != null
&& mTouchView.isOpen()) {
mTouchState = TOUCH_STATE_X;
mTouchView.onSwipe(ev);
}
// find the touched child view
View view = null;
ViewHolder vh = findViewHolderForAdapterPosition(mTouchPosition);
if (vh != null) {
view = vh.itemView;
}
// is not touched the opened menu view, so we intercept this
// touch event
if (mTouchPosition != oldPos && mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
mTouchView = null;
// try to cancel the touch event
MotionEvent cancelEvent = MotionEvent.obtain(ev);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(cancelEvent);
return true;
}
if (view instanceof SuperSwipeMenuLayout) {
mTouchView = (SuperSwipeMenuLayout) view;
mTouchView.setSwipeDirection(mDirection);
}
if (mTouchView != null) {
mTouchView.onSwipe(ev);
}
break;
case MotionEvent.ACTION_MOVE:
dy = Math.abs((ev.getY() - mDownY));
dx = Math.abs((ev.getX() - mDownX));
if (mTouchState == TOUCH_STATE_X && mTouchView.isSwipeEnable()) {
mTouchView.onSwipe(ev);
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
} else if (mTouchState == TOUCH_STATE_NONE && mTouchView.isSwipeEnable()) {
if (Math.abs(dy) > mViewConfiguration.getScaledTouchSlop()) {
mTouchState = TOUCH_STATE_Y;
} else if (dx > mViewConfiguration.getScaledTouchSlop()) {
mTouchState = TOUCH_STATE_X;
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeStart(mTouchPosition);
}
}
}
break;
case MotionEvent.ACTION_UP:
boolean isCloseOnUpEvent = false;
if (mTouchState == TOUCH_STATE_X && mTouchView.isSwipeEnable()) {
isCloseOnUpEvent = !mTouchView.onSwipe(ev);
if (mOnSwipeListener != null) {
mOnSwipeListener.onSwipeEnd(mTouchPosition);
}
if (!mTouchView.isOpen()) {
mTouchPosition = -1;
mTouchView = null;
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
}
long clickDuration = System.currentTimeMillis() - startClickTime;
boolean isOutDuration = clickDuration > ViewConfiguration.getLongPressTimeout();
boolean isOutX = dx > mViewConfiguration.getScaledTouchSlop();
boolean isOutY = dy > mViewConfiguration.getScaledTouchSlop();
// long pressed or scaled touch, we just intercept up touch
// event
if (isOutDuration || isOutX || isOutY) {
return true;
} else {
float eX = ev.getX();
float eY = ev.getY();
View upView = findChildViewUnder(eX, eY);
if (upView instanceof SuperSwipeMenuLayout) {
SuperSwipeMenuLayout smView = (SuperSwipeMenuLayout) upView;
int x = (int) eX - smView.getLeft();
int y = (int) eY - smView.getTop();
View menuView = smView.getMenuView();
final float translationX = ViewCompat.getTranslationX(menuView);
final float translationY = ViewCompat.getTranslationY(menuView);
// intercept the up event when touched on the
// contentView of the opened SwipeMenuLayout
if (!(x >= menuView.getLeft() + translationX &&
x <= menuView.getRight() + translationX &&
y >= menuView.getTop() + translationY &&
y <= menuView.getBottom() + translationY) &&
isCloseOnUpEvent) {
return true;
}
}
}
break;
case MotionEvent.ACTION_CANCEL:
if (mTouchView != null && mTouchView.isSwipeEnable()) {
// when event has canceled, we just consider as up event
ev.setAction(MotionEvent.ACTION_UP);
mTouchView.onSwipe(ev);
}
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* open menu manually
*
* @param position the adapter position
*/
public void smoothOpenMenu(int position) {
View view = mLlm.findViewByPosition(position);
if (view instanceof SuperSwipeMenuLayout) {
mTouchPosition = position;
// close pre opened swipe menu
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
}
mTouchView = (SuperSwipeMenuLayout) view;
mTouchView.setSwipeDirection(mDirection);
mTouchView.smoothOpenMenu();
}
}
/**
* close the opened menu manually
*/
public void smoothCloseMenu() {
// close the opened swipe menu
if (mTouchView != null && mTouchView.isOpen()) {
mTouchView.smoothCloseMenu();
}
}
public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
this.mOnSwipeListener = onSwipeListener;
}
/**
* get current touched view
*
* @return touched view, maybe null
*/
public SuperSwipeMenuLayout getTouchView() {
return mTouchView;
}
public interface OnSwipeListener {
void onSwipeStart(int position);
void onSwipeEnd(int position);
}
/**
* set the swipe direction
*
* @param direction swipe direction (left or right)
*/
public void setSwipeDirection(int direction) {
mDirection = direction;
}
@Override
public void setLayoutManager(LayoutManager layout) {
super.setLayoutManager(layout);
mLlm = layout;
}
}

View File

@ -0,0 +1,32 @@
package com.superrecycleview.superlibrary.sidebar.bean;
import com.superrecycleview.superlibrary.callback.ISuspensionInterface;
/**
* 介绍:索引类的标志位的实体基类 作者zhangxutong 邮箱mcxtzhang@163.com
* CSDNhttp://blog.csdn.net/zxt0601 时间: 16/09/04.
*/
public abstract class BaseIndexBean implements ISuspensionInterface {
private String baseIndexTag;// 所属的分类(城市的汉语拼音首字母)
public String getBaseIndexTag() {
return baseIndexTag;
}
public BaseIndexBean setBaseIndexTag(String baseIndexTag) {
this.baseIndexTag = baseIndexTag;
return this;
}
@Override
public String getSuspensionTag() {
return baseIndexTag;
}
@Override
public boolean isShowSuspension() {
return true;
}
}

View File

@ -0,0 +1,32 @@
package com.superrecycleview.superlibrary.sidebar.bean;
/**
* 介绍:索引类的汉语拼音的接口 作者zhangxutong 邮箱mcxtzhang@163.com
* CSDNhttp://blog.csdn.net/zxt0601 时间: 16/09/04.
*/
public abstract class BaseIndexPinyinBean extends BaseIndexBean {
private String baseIndexPinyin;// 城市的拼音
public String getBaseIndexPinyin() {
return baseIndexPinyin;
}
public BaseIndexPinyinBean setBaseIndexPinyin(String baseIndexPinyin) {
this.baseIndexPinyin = baseIndexPinyin;
return this;
}
// 是否需要被转化成拼音, 类似微信头部那种就不需要 美团的也不需要
// 微信的头部 不需要显示索引
// 美团的头部 索引自定义
// 默认应该是需要的
public boolean isNeedToPinyin() {
return true;
}
// 需要转化成拼音的目标字段
public abstract String getTarget();
}

View File

@ -0,0 +1,27 @@
package com.superrecycleview.superlibrary.sidebar.helper;
import com.superrecycleview.superlibrary.sidebar.bean.BaseIndexPinyinBean;
import java.util.List;
/**
* 介绍IndexBar 的 数据相关帮助类 1 将汉语转成拼音 2 填充indexTag 3 排序源数据源 4
* 根据排序后的源数据源->indexBar的数据源 作者zhangxutong 邮箱mcxtzhang@163.com
* 主页http://blog.csdn.net/zxt0601 时间: 2016/11/28.
*/
public interface IIndexBarDataHelper {
// 汉语-》拼音
IIndexBarDataHelper convert(List<? extends BaseIndexPinyinBean> data);
// 拼音->tag
IIndexBarDataHelper fillInexTag(List<? extends BaseIndexPinyinBean> data);
// 对源数据进行排序RecyclerView
IIndexBarDataHelper sortSourceDatas(List<? extends BaseIndexPinyinBean> datas);
// 对IndexBar的数据源进行排序(右侧栏),在 sortSourceDatas 方法后调用
IIndexBarDataHelper getSortedIndexDatas(List<? extends BaseIndexPinyinBean> sourceDatas,
List<String> datas);
}

View File

@ -0,0 +1,121 @@
package com.superrecycleview.superlibrary.sidebar.helper;
import com.github.promeg.pinyinhelper.Pinyin;
import com.superrecycleview.superlibrary.sidebar.bean.BaseIndexPinyinBean;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 介绍IndexBar 的 数据相关帮助类 实现 * 1 将汉语转成拼音(利用tinyPinyin) 2 填充indexTag (取拼音首字母) 3
* 排序源数据源 4 根据排序后的源数据源->indexBar的数据源 作者zhangxutong 邮箱mcxtzhang@163.com
* 主页http://blog.csdn.net/zxt0601 时间: 2016/11/28.
*/
public class IndexBarDataHelperImpl implements IIndexBarDataHelper {
/**
* 如果需要, 字符->拼音,
*
* @param datas
*/
@Override
public IIndexBarDataHelper convert(List<? extends BaseIndexPinyinBean> datas) {
if (null == datas || datas.isEmpty()) {
return this;
}
int size = datas.size();
for (int i = 0; i < size; i++) {
BaseIndexPinyinBean indexPinyinBean = datas.get(i);
StringBuilder pySb = new StringBuilder();
// add by zhangxutong 2016 11 10 如果不是top 才转拼音,否则不用转了
if (indexPinyinBean.isNeedToPinyin()) {
String target = indexPinyinBean.getTarget();// 取出需要被拼音化的字段
// 遍历target的每个char得到它的全拼音
for (int i1 = 0; i1 < target.length(); i1++) {
// 利用TinyPinyin将char转成拼音
// 查看源码,方法内 如果char为汉字则返回大写拼音
// 如果c不是汉字则返回String.valueOf(c)
pySb.append(Pinyin.toPinyin(target.charAt(i1)).toUpperCase());
}
indexPinyinBean.setBaseIndexPinyin(pySb.toString());// 设置城市名全拼音
} else {
// pySb.append(indexPinyinBean.getBaseIndexPinyin());
}
}
return this;
}
/**
* 如果需要取出,则 取出首字母->tag,或者特殊字母 "#". 否则,用户已经实现设置好
*
* @param datas
*/
@Override
public IIndexBarDataHelper fillInexTag(List<? extends BaseIndexPinyinBean> datas) {
if (null == datas || datas.isEmpty()) {
return this;
}
int size = datas.size();
for (int i = 0; i < size; i++) {
BaseIndexPinyinBean indexPinyinBean = datas.get(i);
if (indexPinyinBean.isNeedToPinyin()) {
// 以下代码设置城市拼音首字母
String tagString = indexPinyinBean.getBaseIndexPinyin().toString().substring(0, 1);
if (tagString.matches("[A-Z]")) {// 如果是A-Z字母开头
indexPinyinBean.setBaseIndexTag(tagString);
} else {// 特殊字母这里统一用#处理
indexPinyinBean.setBaseIndexTag("#");
}
}
}
return this;
}
@Override
public IIndexBarDataHelper sortSourceDatas(List<? extends BaseIndexPinyinBean> datas) {
if (null == datas || datas.isEmpty()) {
return this;
}
convert(datas);
fillInexTag(datas);
// 对数据源进行排序
Collections.sort(datas, new Comparator<BaseIndexPinyinBean>() {
@Override
public int compare(BaseIndexPinyinBean lhs, BaseIndexPinyinBean rhs) {
if (!lhs.isNeedToPinyin()) {
return 0;
} else if (!rhs.isNeedToPinyin()) {
return 0;
} else if (lhs.getBaseIndexTag().equals("#")) {
return 1;
} else if (rhs.getBaseIndexTag().equals("#")) {
return -1;
} else {
return lhs.getBaseIndexPinyin().compareTo(rhs.getBaseIndexPinyin());
}
}
});
return this;
}
@Override
public IIndexBarDataHelper getSortedIndexDatas(List<? extends BaseIndexPinyinBean> sourceDatas,
List<String> indexDatas) {
if (null == sourceDatas || sourceDatas.isEmpty()) {
return this;
}
// 按数据源来 此时sourceDatas 已经有序
int size = sourceDatas.size();
String baseIndexTag;
for (int i = 0; i < size; i++) {
baseIndexTag = sourceDatas.get(i).getBaseIndexTag();
if (!indexDatas.contains(baseIndexTag)) {// 则判断是否已经将这个索引添加进去,若没有则添加
indexDatas.add(baseIndexTag);
}
}
return this;
}
}

View File

@ -0,0 +1,393 @@
package com.superrecycleview.superlibrary.sidebar.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.superrecycleview.superlibrary.R;
import com.superrecycleview.superlibrary.sidebar.bean.BaseIndexPinyinBean;
import com.superrecycleview.superlibrary.sidebar.helper.IIndexBarDataHelper;
import com.superrecycleview.superlibrary.sidebar.helper.IndexBarDataHelperImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 介绍:索引右侧边栏 作者zhangxutong 邮箱mcxtzhang@163.com
* CSDNhttp://blog.csdn.net/zxt0601 时间: 16/09/04.
*/
public class SuperSideBar extends View {
private static final String TAG = "zxt/IndexBar";
// #在最后面(默认的数据源)
public static String[] INDEX_STRING = {
"A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z", "#"
};
// 是否需要根据实际的数据来生成索引数据源(例如 只有 A B C 三种tag那么索引栏就 A B C 三项)
private boolean isNeedRealIndex;
// 索引数据源
private List<String> mIndexDatas;
// 每个index区域的高度
private int mGapHeight;
private Paint mPaint;
private int offsetY;
/**
* sideBar的字母size
*/
private int sideBarTextSize;
/**
* sideBar的背景颜色
*/
private int sideBarBg = android.R.color.transparent;
/**
* 按下的是显示的背景色
*/
private int sideBarPressBg = android.R.color.transparent;
/**
* indexBar的字母颜色
*/
private int sideBarTextColor = android.R.color.darker_gray;
/**
* indexBar选中的字母颜色
*/
private int sideBarTextColorPress = android.R.color.holo_blue_dark;
/**
* 该字母是否选中
*/
private int choose = -1;// 选中
// 以下是帮助类
// 汉语->拼音,拼音->tag
private IIndexBarDataHelper mDataHelper;
// 以下边变量是外部set进来的
private TextView mPressedShowTextView;// 用于特写显示正在被触摸的index值
private boolean isSourceDatasAlreadySorted;// 源数据 已经有序?
private List<? extends BaseIndexPinyinBean> mSourceDatas;// Adapter的数据源
private LinearLayoutManager mLayoutManager;
private int mHeaderViewCount = 0;
public SuperSideBar(Context context) {
this(context, null);
}
public SuperSideBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SuperSideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public int getHeaderViewCount() {
return mHeaderViewCount;
}
/**
* 设置Headerview的Count
*
* @param headerViewCount
* @return
*/
public SuperSideBar setHeaderViewCount(int headerViewCount) {
mHeaderViewCount = headerViewCount;
return this;
}
public boolean isSourceDatasAlreadySorted() {
return isSourceDatasAlreadySorted;
}
/**
* 源数据 是否已经有序
*
* @param sourceDatasAlreadySorted
* @return
*/
public SuperSideBar setSourceDatasAlreadySorted(boolean sourceDatasAlreadySorted) {
isSourceDatasAlreadySorted = sourceDatasAlreadySorted;
return this;
}
public IIndexBarDataHelper getDataHelper() {
return mDataHelper;
}
/**
* 设置数据源帮助类
*
* @param dataHelper
* @return
*/
public SuperSideBar setDataHelper(IIndexBarDataHelper dataHelper) {
mDataHelper = dataHelper;
return this;
}
/**
* 初始化
*
* @param context
* @param attrs
* @param defStyleAttr
*/
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
sideBarTextSize = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics());// 默认的TextSize
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.SuperSideBar, defStyleAttr, 0);
int n = typedArray.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
if (attr == R.styleable.SuperSideBar_super_sidebar_textsize) {
sideBarTextSize = typedArray.getDimensionPixelSize(attr, sideBarTextSize);
} else if (attr == R.styleable.SuperSideBar_super_sidebar_backgrond_press) {
sideBarPressBg = typedArray.getColor(attr, sideBarPressBg);
} else if (attr == R.styleable.SuperSideBar_super_sidebar_textcolor) {
sideBarTextColor = typedArray.getColor(attr, sideBarTextColor);
} else if (attr == R.styleable.SuperSideBar_super_sidebar_textcolor_press) {
sideBarTextColorPress = typedArray.getColor(attr, sideBarTextColorPress);
}
}
typedArray.recycle();
initIndexDatas();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(sideBarTextSize);
mPaint.setColor(Color.BLACK);
// 设置index触摸监听器
setmOnIndexPressedListener(new onIndexPressedListener() {
@Override
public void onIndexPressed(int index, String text) {
if (mPressedShowTextView != null) { // 显示hintTexView
mPressedShowTextView.setVisibility(View.VISIBLE);
mPressedShowTextView.setText(text);
}
// 滑动Rv
if (mLayoutManager != null) {
int position = getPosByTag(text);
if (position != -1) {
mLayoutManager.scrollToPositionWithOffset(position, 0);
}
}
}
@Override
public void onMotionEventEnd() {
// 隐藏hintTextView
if (mPressedShowTextView != null) {
mPressedShowTextView.setVisibility(View.GONE);
}
}
});
mDataHelper = new IndexBarDataHelperImpl();
}
@Override
protected void onDraw(Canvas canvas) {
if (mIndexDatas.isEmpty())
return;
int height = getHeight();// 获取对应的高度
int width = getWidth();// 获取对应的宽度
mGapHeight = height / mIndexDatas.size();// 获取每个字母的高度
int dp24 = dip2px(24);
mGapHeight = mGapHeight > dp24 ? dp24 : mGapHeight;
offsetY = (height - mGapHeight * mIndexDatas.size()) / 2;
for (int i = 0; i < mIndexDatas.size(); i++) {
int colorId = i == choose ? android.R.color.holo_blue_bright
: sideBarTextColor;
mPaint.setColor(ContextCompat.getColor(getContext(), colorId));
mPaint.setAntiAlias(true);
mPaint.setTextSize(sideBarTextSize);
mPaint.setTypeface(Typeface.DEFAULT);
float xPos = width / 2 - mPaint.measureText(mIndexDatas.get(i)) / 2;
float yPos = offsetY + mGapHeight * (i + 0.5F);
if (i == choose) {
// 选中的状态
mPaint.setFakeBoldText(true);
canvas.drawText(mIndexDatas.get(i), dip2px(-56), yPos, mPaint);
}
canvas.drawText(mIndexDatas.get(i), xPos, yPos, mPaint);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldChoose = choose;
// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
final int c = (int) ((y - offsetY) / mGapHeight);
switch (action) {
case MotionEvent.ACTION_DOWN:
setBackgroundResource(sideBarPressBg);// 手指按下时背景变色
// 注意这里没有break因为down时也要计算落点 回调监听器
case MotionEvent.ACTION_MOVE:
if (oldChoose != c) {
if (c >= 0 && c < mIndexDatas.size()) {
choose = c;
// 回调监听器
if (null != mOnIndexPressedListener) {
mOnIndexPressedListener.onIndexPressed(c, mIndexDatas.get(c));
}
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
default:
choose = -1;
setBackgroundResource(sideBarBg);
if (null != mOnIndexPressedListener) {
mOnIndexPressedListener.onMotionEventEnd();
}
invalidate();
break;
}
return true;
}
private static int dip2px(float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (null == mIndexDatas || mIndexDatas.isEmpty()) {
return;
}
}
/**
* 当前被按下的index的监听器
*/
public interface onIndexPressedListener {
void onIndexPressed(int index, String text);// 当某个Index被按下
void onMotionEventEnd();// 当触摸事件结束UP CANCEL
}
private onIndexPressedListener mOnIndexPressedListener;
public onIndexPressedListener getmOnIndexPressedListener() {
return mOnIndexPressedListener;
}
public void setmOnIndexPressedListener(onIndexPressedListener mOnIndexPressedListener) {
this.mOnIndexPressedListener = mOnIndexPressedListener;
}
/**
* 显示当前被按下的index的TextView
*
* @return
*/
public SuperSideBar setmPressedShowTextView(TextView mPressedShowTextView) {
this.mPressedShowTextView = mPressedShowTextView;
return this;
}
public SuperSideBar setmLayoutManager(LinearLayoutManager mLayoutManager) {
this.mLayoutManager = mLayoutManager;
return this;
}
/**
* 一定要在设置数据源{@link #setSourceDatas(List)}之前调用
*
* @param needRealIndex
* @return
*/
public SuperSideBar setNeedRealIndex(boolean needRealIndex) {
isNeedRealIndex = needRealIndex;
initIndexDatas();
return this;
}
private void initIndexDatas() {
if (isNeedRealIndex) {
mIndexDatas = new ArrayList<>();
} else {
mIndexDatas = Arrays.asList(INDEX_STRING);
}
}
public SuperSideBar setSourceDatas(List<? extends BaseIndexPinyinBean> mSourceDatas) {
this.mSourceDatas = mSourceDatas;
initSourceDatas();// 对数据源进行初始化
invalidate();
return this;
}
/**
* 初始化原始数据源,并取出索引数据源
*
* @return
*/
private void initSourceDatas() {
// add by zhangxutong 2016 09 08 :解决源数据为空 或者size为0的情况,
if (null == mSourceDatas || mSourceDatas.isEmpty()) {
return;
}
if (!isSourceDatasAlreadySorted) {
// 排序sourceDatas
mDataHelper.sortSourceDatas(mSourceDatas);
} else {
// 汉语->拼音
mDataHelper.convert(mSourceDatas);
// 拼音->tag
mDataHelper.fillInexTag(mSourceDatas);
}
if (isNeedRealIndex) {
mDataHelper.getSortedIndexDatas(mSourceDatas, mIndexDatas);
}
}
/**
* 根据传入的pos返回tag
*
* @param tag
* @return
*/
private int getPosByTag(String tag) {
// add by zhangxutong 2016 09 08 :解决源数据为空 或者size为0的情况,
if (null == mSourceDatas || mSourceDatas.isEmpty()) {
return -1;
}
if (TextUtils.isEmpty(tag)) {
return -1;
}
for (int i = 0; i < mSourceDatas.size(); i++) {
if (tag.equals(mSourceDatas.get(i).getBaseIndexTag())) {
return i + getHeaderViewCount();
}
}
return -1;
}
}

View File

@ -0,0 +1,113 @@
/**
* Copyright 2016 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.superrecycleview.superlibrary.utils;
import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorRes;
import androidx.annotation.DimenRes;
import android.util.Log;
import android.util.TypedValue;
import java.util.List;
/**
* Created by super南仔 on 07/28/16. <br/>
* blog: http://supercwn.github.io/ <br/>
* GitHub: https://github.com/supercwn
*/
public class SuperAdapterUtil {
private static final Application sApp;
static {
Application app = null;
try {
app = (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null);
if (app == null)
throw new IllegalStateException(
"Static initialization of Applications must be on main thread.");
} catch (final Exception e) {
Log.e(SuperAdapterUtil.class.getSimpleName(),
"Failed to get current application from AppGlobals." + e.getMessage());
try {
app = (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null);
} catch (final Exception ex) {
Log.e(SuperAdapterUtil.class.getSimpleName(),
"Failed to get current application from ActivityThread." + e.getMessage());
}
} finally {
sApp = app;
}
}
private SuperAdapterUtil() {
}
public static Application getApp() {
return sApp;
}
public static int dp2px(float dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue,
getApp().getResources().getDisplayMetrics());
}
public static int sp2px(float dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dpValue,
getApp().getResources().getDisplayMetrics());
}
public static int getDimensionPixelOffset(@DimenRes int resId) {
return getApp().getResources().getDimensionPixelOffset(resId);
}
public static int getColor(@ColorRes int resId) {
return getApp().getResources().getColor(resId);
}
public static Drawable rotateBitmap(Bitmap inputBitmap) {
Matrix matrix = new Matrix();
matrix.setRotate(90, (float) inputBitmap.getWidth() / 2,
(float) inputBitmap.getHeight() / 2);
float outputX = inputBitmap.getHeight();
float outputY = 0;
final float[] values = new float[9];
matrix.getValues(values);
float x1 = values[Matrix.MTRANS_X];
float y1 = values[Matrix.MTRANS_Y];
matrix.postTranslate(outputX - x1, outputY - y1);
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap.getHeight(), inputBitmap.getWidth(),
Bitmap.Config.ARGB_8888);
Paint paint = new Paint();
Canvas canvas = new Canvas(outputBitmap);
canvas.drawBitmap(inputBitmap, matrix, paint);
return new BitmapDrawable(null, outputBitmap);
}
public static boolean isListNotEmpty(List list) {
return list != null && !list.isEmpty();
}
}

View File

@ -0,0 +1,291 @@
/**
* Copyright 2016 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.superrecycleview.superlibrary.utils;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntRange;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
import com.superrecycleview.superlibrary.R;
/**
* Created by super南仔 on 07/28/16. <br/>
* blog: http://supercwn.github.io/ <br/>
* GitHub: https://github.com/supercwn <br/>
* RecyclerView 分隔线
*/
public class SuperDivider extends RecyclerView.ItemDecoration {
private Drawable mDividerDrawable;
private int mMarginLeft;
private int mMarginRight;
private int mOrientation = LinearLayout.VERTICAL;// 暂时只考虑竖直列表不考虑横向列表以及Grid模式
private int mStartSkipCount = 2;// 默认列表第一个没有仅仅是在使用SuperRecycleView的情况下
private int mEndSkipCount = 1;// 默认列表最后一个没有仅仅是在使用SuperRecycleView的情况下
private int mSize = 1;
private SuperDivider(@DrawableRes int drawableResId) {
mDividerDrawable = ContextCompat.getDrawable(SuperAdapterUtil.getApp(), drawableResId);
mSize = Math.min(mDividerDrawable.getIntrinsicHeight(),
mDividerDrawable.getIntrinsicWidth());
}
/**
* 自定义 drawable 资源分隔线
*
* @param drawableResId
* @return
*/
public static SuperDivider newDrawableDivider(@DrawableRes int drawableResId) {
return new SuperDivider(drawableResId);
}
/**
* 默认的 shape 资源分隔线
*
* @return
*/
public static SuperDivider newShapeDivider() {
return new SuperDivider(R.drawable.bga_adapter_divider_shape);
}
/**
* 默认的图片分隔线
*
* @return
*/
public static SuperDivider newBitmapDivider() {
return new SuperDivider(R.mipmap.bga_adapter_divider_bitmap);
}
/**
* 设置左边距和右边距
*
* @param bothMarginDp 单位为 dp
* @return
*/
public SuperDivider setBothMarginDp(int bothMarginDp) {
mMarginLeft = SuperAdapterUtil.dp2px(bothMarginDp);
mMarginRight = mMarginLeft;
return this;
}
/**
* 设置左边距
*
* @param marginLeftDp 单位为 dp
* @return
*/
public SuperDivider setMarginLeftDp(int marginLeftDp) {
mMarginLeft = SuperAdapterUtil.dp2px(marginLeftDp);
return this;
}
/**
* 设置右边距
*
* @param marginRightDp 单位为 dp
* @return
*/
public SuperDivider setMarginRightDp(int marginRightDp) {
mMarginRight = SuperAdapterUtil.dp2px(marginRightDp);
return this;
}
/**
* 设置分隔线颜色资源 id
*
* @param resId
* @return
*/
public SuperDivider setColor(@ColorRes int resId) {
return setColorResource(SuperAdapterUtil.getColor(resId));
}
/**
* 设置分隔线颜色
*
* @param color
* @return
*/
private SuperDivider setColorResource(@ColorInt int color) {
mDividerDrawable.setColorFilter(color, PorterDuff.Mode.SRC);
return this;
}
/**
* 设置为水平方向
*
* @return
*/
public SuperDivider setHorizontal() {
mOrientation = LinearLayout.HORIZONTAL;
return this;
}
/**
* 旋转分隔线,仅用于分隔线为 Bitmap 时。应用场景UI 给了一个水平分隔线,恰巧项目里需要一条一模一样的竖直分隔线
*
* @return
*/
public SuperDivider rotateDivider() {
if (mDividerDrawable != null && mDividerDrawable instanceof BitmapDrawable) {
mDividerDrawable = SuperAdapterUtil
.rotateBitmap(((BitmapDrawable) mDividerDrawable).getBitmap());
}
return this;
}
/**
* 跳过开始的条数。默认值为 1不绘制第一个 item 顶部的分隔线
*
* @param startSkipCount
* @return
*/
public SuperDivider setStartSkipCount(@IntRange(from = 0) int startSkipCount) {
mStartSkipCount = startSkipCount;
if (mStartSkipCount < 0) {
mStartSkipCount = 0;
}
return this;
}
/**
* 跳过末尾的条数
*
* @param endSkipCount
* @return
*/
public SuperDivider setEndSkipCount(@IntRange(from = 0) int endSkipCount) {
mEndSkipCount = endSkipCount;
if (mEndSkipCount < 0) {
mEndSkipCount = 0;
}
return this;
}
/**
* 设置分割线尺寸
*
* @param sizeDp 单位为 dp
* @return
*/
public SuperDivider setSizeDp(int sizeDp) {
mSize = SuperAdapterUtil.dp2px(sizeDp);
return this;
}
private boolean isNeedSkip(int childAdapterPosition,
int realItemCount) {
int lastPosition = realItemCount - 1;
// 跳过最后 mEndSkipCount 个
if (childAdapterPosition > lastPosition - mEndSkipCount) {
return true;
}
// 跳过前 mStartSkipCount 个
// 1
if (childAdapterPosition < mStartSkipCount) {
return true;
}
// 默认不跳过
return false;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (parent.getLayoutManager() == null || parent.getAdapter() == null) {
return;
}
int childAdapterPosition = parent.getChildAdapterPosition(view);
int itemCount = parent.getAdapter().getItemCount();
int realChildAdapterPosition = childAdapterPosition;
int realItemCount = itemCount;
if (isNeedSkip(childAdapterPosition, realItemCount)) {
outRect.set(0, 0, 0, 0);
} else {
if (mOrientation == LinearLayout.VERTICAL) {
getVerticalItemOffsets(outRect);
} else {
outRect.set(mSize, 0, 0, 0);
}
}
}
public void getVerticalItemOffsets(Rect outRect) {
outRect.set(0, mSize, 0, 0);
}
@Override
public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null || parent.getAdapter() == null) {
return;
}
int itemCount = parent.getAdapter().getItemCount();
if (mOrientation == LinearLayout.VERTICAL) {
drawVertical(canvas, parent, itemCount);
} else {
drawVertical(canvas, parent, itemCount);
}
}
private void drawVertical(Canvas canvas, RecyclerView parent, int itemCount) {
int dividerLeft = parent.getPaddingLeft() + mMarginLeft;
int dividerRight = parent.getWidth() - parent.getPaddingRight() - mMarginRight;
View itemView;
RecyclerView.LayoutParams itemLayoutParams;
for (int childPosition = 0; childPosition < itemCount; childPosition++) {
itemView = parent.getChildAt(childPosition);
if (itemView == null || itemView.getLayoutParams() == null) {
continue;
}
int childAdapterPosition = parent.getChildAdapterPosition(itemView);
if (isNeedSkip(childAdapterPosition, itemCount)) {
continue;
}
itemLayoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams();
int dividerBottom = itemView.getTop() - itemLayoutParams.topMargin;
drawVertical(canvas, dividerLeft, dividerRight, dividerBottom);
}
}
public void drawVertical(Canvas canvas, int dividerLeft, int dividerRight, int itemTop) {
int dividerBottom = itemTop;
int dividerTop = dividerBottom - mSize;
mDividerDrawable.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
mDividerDrawable.draw(canvas);
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#DEDEDE" />
<size
android:width="1dp"
android:height="1dp" />
</shape>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list android:oneshot="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:duration="100" android:drawable="@mipmap/loading_01" />
<item android:duration="100" android:drawable="@mipmap/loading_02" />
<item android:duration="100" android:drawable="@mipmap/loading_03" />
<item android:duration="100" android:drawable="@mipmap/loading_04" />
<item android:duration="100" android:drawable="@mipmap/loading_05" />
<item android:duration="100" android:drawable="@mipmap/loading_06" />
<item android:duration="100" android:drawable="@mipmap/loading_07" />
<item android:duration="100" android:drawable="@mipmap/loading_08" />
<item android:duration="100" android:drawable="@mipmap/loading_09" />
<item android:duration="100" android:drawable="@mipmap/loading_10" />
<item android:duration="100" android:drawable="@mipmap/loading_11" />
<item android:duration="100" android:drawable="@mipmap/loading_12" />
</animation-list>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate android:drawable="@mipmap/ic_loading_rotate" android:pivotX="50.0%" android:pivotY="50.0%"
xmlns:android="http://schemas.android.com/apk/res/android" />

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="3dp">
<ProgressBar
android:id="@+id/listview_foot_progress"
android:layout_width="30dip"
android:layout_height="30dip"
/>
<TextView
android:id="@+id/listview_foot_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="加载中..."/>
</LinearLayout>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="bottom" >
<RelativeLayout
android:id="@+id/listview_header_content"
android:layout_width="fill_parent"
android:layout_height="80dp"
android:paddingTop="10dip"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="100dip"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical"
android:id="@+id/listview_header_text">
<TextView
android:id="@+id/refresh_status_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/listview_header_hint_normal" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginTop="3dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/listview_header_last_time"
android:textSize="12sp" />
<TextView
android:id="@+id/last_refresh_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/listview_header_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="35dp"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/listview_header_text"
android:src="@mipmap/ic_pulltorefresh_arrow" />
<com.superrecycleview.superlibrary.recycleview.SimpleViewSwitcher
android:id="@+id/listview_header_progressbar"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_toLeftOf="@id/listview_header_text"
android:layout_centerVertical="true"
android:layout_marginLeft="40dp"
android:layout_marginRight="10dp"
android:visibility="invisible" />
</RelativeLayout>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--pull to refresh and loadmore-->
<declare-styleable name="AVLoadingIndicatorView">
<attr name="indicator">
<flag name="BallPulse" value="0" />
<flag name="BallGridPulse" value="1" />
<flag name="BallClipRotate" value="2" />
<flag name="BallClipRotatePulse" value="3" />
<flag name="SquareSpin" value="4" />
<flag name="BallClipRotateMultiple" value="5" />
<flag name="BallPulseRise" value="6" />
<flag name="BallRotate" value="7" />
<flag name="CubeTransition" value="8" />
<flag name="BallZigZag" value="9" />
<flag name="BallZigZagDeflect" value="10" />
<flag name="BallTrianglePath" value="11" />
<flag name="BallScale" value="12" />
<flag name="LineScale" value="13" />
<flag name="LineScaleParty" value="14" />
<flag name="BallScaleMultiple" value="15" />
<flag name="BallPulseSync" value="16" />
<flag name="BallBeat" value="17" />
<flag name="LineScalePulseOut" value="18" />
<flag name="LineScalePulseOutRapid" value="19" />
<flag name="BallScaleRipple" value="20" />
<flag name="BallScaleRippleMultiple" value="21" />
<flag name="BallSpinFadeLoader" value="22" />
<flag name="LineSpinFadeLoader" value="23" />
<flag name="TriangleSkewSpin" value="24" />
<flag name="Pacman" value="25" />
<flag name="BallGridBeat" value="26" />
<flag name="SemiCircleSpin" value="27" />
</attr>
<attr name="indicator_color" format="color" />
</declare-styleable>
<!-- end -->
<!--swipeMenu-->
<declare-styleable name="SwipeMenu">
<attr name="anim_duration" format="integer" />
</declare-styleable>
<!--end-->
<!--indexbar-->
<declare-styleable name="SuperSideBar">
<!--bar的字体大小-->
<attr name="super_sidebar_textsize" format="dimension" />
<!--bar的背景颜色-->
<attr name="super_sidebar_backgrond" format="color" />
<!--bar点击后的背景颜色-->
<attr name="super_sidebar_backgrond_press" format="color" />
<!--bar字体的颜色-->
<attr name="super_sidebar_textcolor" format="color" />
<!--bar点击后的字体颜色-->
<attr name="super_sidebar_textcolor_press" format="color" />
</declare-styleable>
<!--end-->
</resources>

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#419DE4</color>
<color name="colorAccent">#FF4081</color>
<color name="black">#000000</color>
<color name="white">#fff</color>
<color name="blue">#419DE4</color>
<color name="border_clo">#ccc</color>
<color name="border_clo1">#EA0000</color>
<color name="text_clo">#333</color>
<color name="light_text_color">#ffd7d7d7</color>
<!-- 搜索框 -->
<color name="myred">#2894ff</color>
<color name="darkgray">#cccc</color>
<color name="color_white">#ffffff</color>
<color name="btn_press_color1">#5677fc</color>
<color name="btn_pressed_color">#3ca0ec</color>
<color name="btn_press_color">#66000000</color>
<color name="btn_unpress_color">#22000000</color>
<!-- 信鸽 -->
<color name="ccp_green">#ff00B486</color>
<color name="gray">#858585</color>
<color name="gray_t">#7f9a9b9c</color>
<color name="gray_presentation">#B7B7B7</color>
<color name="fuxk_base_color_white">#ffffff</color>
<color name="fuxk_base_divide_line_color">#dcdcdc</color>
<!-- 绿色 -->
<color name="green">#45CD87</color>
<color name="green01">#B8EFF9</color>
<color name="green02">#59E1FA</color>
<color name="green03">#C9FFDE</color>
<color name="green04">#31F77E</color>
<color name="green05">#9BECF9</color>
<!-- 紫色 -->
<color name="violet01">#DB96FF</color>
<color name="violet02">#BE55F5</color>
<color name="violet03">#CBC3EE</color>
<color name="violet04">#937FEF</color>
<!-- 浅黄绿 -->
<color name="yellowGreen01">#D6FB9C</color>
<color name="yellowGreen02">#A9F82E</color>
<color name="yellow01">#FCDB70</color>
<color name="yellow02">#F8C82E</color>
<!-- 红紫 -->
<color name="red">#ffff5454</color>
<color name="red01">#F9B2C0</color>
<color name="red02">#F6728C</color>
<color name="redViolet01">#F6D1F3</color>
<color name="redViolet02">#F6728C</color>
<!-- 灰色 -->
<color name="gray01">#DADDE2</color>
<color name="gray02">#A3A1A1</color>
<color name="gray03">#9FA2A3</color>
<color name="gray04">#757778</color>
<color name="gray05">#95A3AE</color>
<color name="gray06">#f5f5f5</color>
<color name="gray07">#838181</color>
<color name="gray_black">#727272</color>
<color name="gray_patrol">#EDEDED</color>
<!-- 半透明 -->
<color name="translucent01">#55000000</color>
<color name="translucent02">#97000000</color>
<color name="lucen">#00000000</color>
<color name="forgetpwd">#91B1C7</color>
<color name="pa_item_bg">#E9E9E9</color>
<color name="fav_click_color">#66000000</color>
<color name="sign_mood_selected">#ff9B9B9C</color>
<color name="sign_mood_nomal">#00000000</color>
<color name="sign_mood_nomal_0">#e0e0e0</color>
<color name="picker_white">#ffffff</color>
<color name="picker_bg_floder_press">#bdbdbd</color>
<color name="list_bg_gaoliang">#E1FFFF</color>
<color name="bar">#31F77E</color>
<color name="bar1">#B5C2CA</color>
<color name="bar2">#F5E6BF</color>
<color name="bar3">#81D8C8</color>
<color name="color_gr_white">#7fffffff</color>
<color name="color_luc_blue">#661774ac</color>
<color name="color_luc_blue_login">#cc01b4ed</color>
<color name="app_title_color_second">#FFFFFF</color>
<color name="gray_text">#A8A8A8</color>
<color name="app_title_color_first">#01b4ee</color>
<color name="description">#686868</color>
<color name="line">#4DD4B3</color>
<color name="linefill">#DEEFE4</color>
<color name="line1">#E9C517</color>
<color name="line1fill">#ECEAD0</color>
<color name="line2">#9F8FBA</color>
<color name="line2fill">#EBE4F8</color>
<color name="result_view">#b0000000</color>
<color name="transparent">#00000000</color>
<color name="ccp_green_alpha">#7f00B486</color>
<item name="green_btn_color_disable" type="color">@color/ccp_green_alpha</item>
<color name="green_btn_color_pressed">#ff298409</color>
<color name="red_btn_color_normal">#ffff5454</color>
<color name="red_btn_color_pressed">#ffd24242</color>
<color name="red_btn_color_disable">#7fff5454</color>
<color name="error_red">#E0796C</color>
<color name="error_yellow">#FF8400</color>
<color name="title_blue">#43B3FF</color>
<color name="blues">#66b3ff</color>
<color name="little_blue">#43B3FF</color>
<color name="viewfinder_mask">#60000000</color>
<color name="possible_result_points">#c0ffff00</color>
<color name="btn_normal_unpressed_bg">#2CC8F2</color>
<color name="btn_normal_pressed_bg">#2CA6E0</color>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<item name="smContentView" type="id"/>
<item name="smMenuView" type="id"/>
<item name="BaseQuickAdapter_viewholder_support" type="id"/>
<item name="BaseQuickAdapter_swiping_support" type="id"/>
<item name="BaseQuickAdapter_dragging_support" type="id"/>
</resources>

View File

@ -0,0 +1,11 @@
<resources>
<string name="app_name">SuperLibrary</string>
<string name="listview_header_hint_normal">下拉刷新</string>
<string name="listview_header_hint_release">释放立即刷新</string>
<string name="listview_loading">正在加载</string>
<string name="nomore_loading">没有了</string>
<string name="refreshing">正在刷新</string>
<string name="refresh_done">刷新完成</string>
<string name="listview_header_last_time">上次更新时间:</string>
</resources>

View File

@ -0,0 +1,15 @@
package com.superrecycleview.superlibrary;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}