diff --git a/recyclerview_helper/.idea/inspectionProfiles/Project_Default.xml b/recyclerview_helper/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..a6cb5d5 --- /dev/null +++ b/recyclerview_helper/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/recyclerview_helper/.idea/inspectionProfiles/profiles_settings.xml b/recyclerview_helper/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/recyclerview_helper/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/AllActivity.java b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/AllActivity.java new file mode 100644 index 0000000..071b953 --- /dev/null +++ b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/AllActivity.java @@ -0,0 +1,159 @@ +package com.lvr.recyclerview_helper.sample; + +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import com.lvr.library.adapter.CommonAdapter; +import com.lvr.library.anims.adapters.ScaleInAnimationAdapter; +import com.lvr.library.anims.animators.LandingAnimator; +import com.lvr.library.holder.BaseViewHolder; +import com.lvr.library.recyclerview.HRecyclerView; +import com.lvr.library.recyclerview.OnLoadMoreListener; +import com.lvr.library.recyclerview.OnRefreshListener; +import com.lvr.recyclerview_helper.R; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Created by lvr on 2017/5/25. + */ +public class AllActivity extends AppCompatActivity implements OnLoadMoreListener, OnRefreshListener { + private String[] mStrings = { + "Apple", "Ball", "Camera", "Day", "Egg", "Foo", "Google", "Hello", "Iron", "Japan", "Coke", + "Dog", "Cat", "Yahoo", "Sony", "Canon", "Fujitsu", "USA", "Nexus", "LINE", "Haskell", "C++", + "Go" + }; + private int[] mImageRes = {R.drawable.image1, R.drawable.image2}; + private HRecyclerView mRecyclerView; + private LoadMoreFooterView mLoadMoreFooterView; + private Handler mHandler = new Handler(){ + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + if(msg.what==0){ + //刷新完毕 + mRecyclerView.setRefreshing(false); + }else if(count==4){ + //没有更多数据 + mLoadMoreFooterView.setStatus(LoadMoreFooterView.Status.THE_END); + }else{ + //加载更多完毕 + mDatas.add("Alibaba"); + mDatas.add("Java"); + mDatas.add("Kotlin"); + mDatas.add("Swift"); + mAdapter.notifyItemInserted(mDatas.size()+1); + mLoadMoreFooterView.setStatus(LoadMoreFooterView.Status.GONE); + } + } + }; + private List mDatas; + private CommonAdapter mAdapter; + private int count =1; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_all); + mRecyclerView = (HRecyclerView) findViewById(R.id.list); + mDatas = new ArrayList<>(); + for(int i=0;i(this, R.layout.item_common, mDatas) { + @Override + public void convert(BaseViewHolder holder, int position) { + holder.setText(R.id.tv_content,mDatas.get(position)); + int number = new Random().nextInt(2); + holder.setImageResource(R.id.iv_content,mImageRes[number]); + } + }; + + mRecyclerView.setItemAnimator(new LandingAnimator()); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + ScaleInAnimationAdapter scaleAdapter = new ScaleInAnimationAdapter(mAdapter); + scaleAdapter.setFirstOnly(false); + scaleAdapter.setDuration(500); + mRecyclerView.setAdapter(scaleAdapter); + mAdapter.setOnItemClickListener(new CommonAdapter.OnItemClickListener() { + @Override + public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) { + System.out.println("点击"); + showMyDialog("响应点击事件"); + } + }); + mAdapter.setOnItemLongClickListener(new CommonAdapter.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) { + showMyDialog("响应长按事件"); + return false; + } + }); + mRecyclerView.setOnRefreshListener(this); + mRecyclerView.setOnLoadMoreListener(this); + mLoadMoreFooterView = (LoadMoreFooterView) mRecyclerView.getLoadMoreFooterView(); + mRecyclerView.post(new Runnable() { + @Override + public void run() { + mRecyclerView.setRefreshing(true); + } + }); + } + + public void showMyDialog(String message){ + AlertDialog.Builder builder = new AlertDialog.Builder(AllActivity.this); + builder.setTitle("事件类型"); + builder.setMessage(message); + AlertDialog dialog = builder.create(); + builder.setNegativeButton("知道", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + dialog.show(); + } + + @Override + public void onLoadMore() { + if(mLoadMoreFooterView.canLoadMore()){ + mLoadMoreFooterView.setStatus(LoadMoreFooterView.Status.LOADING); + new Thread(new Runnable() { + @Override + public void run() { + //假装加载耗时数据 + SystemClock.sleep(1000); + Message message = Message.obtain(); + message.what =count; + count++; + mHandler.sendMessage(message); + } + }).start(); + } + + } + + @Override + public void onRefresh() { + new Thread(new Runnable() { + @Override + public void run() { + //假装加载耗时数据 + SystemClock.sleep(1000); + Message msg = Message.obtain(); + msg.what=0; + mHandler.sendMessage(msg); + } + }).start(); + + } +} diff --git a/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/ClassicRefreshHeaderView.java b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/ClassicRefreshHeaderView.java new file mode 100644 index 0000000..f1645be --- /dev/null +++ b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/ClassicRefreshHeaderView.java @@ -0,0 +1,121 @@ +package com.lvr.recyclerview_helper.sample; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.lvr.library.recyclerview.RefreshTrigger; +import com.lvr.recyclerview_helper.R; + + +public class ClassicRefreshHeaderView extends RelativeLayout implements RefreshTrigger { + private ImageView ivArrow; + + private ImageView ivSuccess; + + private TextView tvRefresh; + + private ProgressBar progressBar; + + private Animation rotateUp; + + private Animation rotateDown; + + private boolean rotated = false; + + private int mHeight; + + public ClassicRefreshHeaderView(Context context) { + this(context, null); + } + + public ClassicRefreshHeaderView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClassicRefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + inflate(context, R.layout.layout_hrecyclerview_classic_refresh_header_view, this); + + tvRefresh = (TextView) findViewById(R.id.tvRefresh); + + ivArrow = (ImageView) findViewById(R.id.ivArrow); + + ivSuccess = (ImageView) findViewById(R.id.ivSuccess); + + progressBar = (ProgressBar) findViewById(R.id.progressbar); + + rotateUp = AnimationUtils.loadAnimation(context, R.anim.rotate_up); + + rotateDown = AnimationUtils.loadAnimation(context, R.anim.rotate_down); + } + + @Override + public void onStart(boolean automatic, int headerHeight, int finalHeight) { + this.mHeight = headerHeight; + progressBar.setIndeterminate(false); + } + + @Override + public void onMove(boolean isComplete, boolean automatic, int moved) { + if (!isComplete) { + ivArrow.setVisibility(VISIBLE); + progressBar.setVisibility(GONE); + ivSuccess.setVisibility(GONE); + if (moved <= mHeight) { + if (rotated) { + ivArrow.clearAnimation(); + ivArrow.startAnimation(rotateDown); + rotated = false; + } + tvRefresh.setText("下拉刷新"); + } else { + tvRefresh.setText("释放刷新"); + if (!rotated) { + ivArrow.clearAnimation(); + ivArrow.startAnimation(rotateUp); + rotated = true; + } + } + } + } + + @Override + public void onRefresh() { + ivSuccess.setVisibility(GONE); + ivArrow.clearAnimation(); + ivArrow.setVisibility(GONE); + progressBar.setVisibility(VISIBLE); + tvRefresh.setText("正在刷新"); + } + + @Override + public void onRelease() { + + } + + @Override + public void onComplete() { + rotated = false; + ivSuccess.setVisibility(VISIBLE); + ivArrow.clearAnimation(); + ivArrow.setVisibility(GONE); + progressBar.setVisibility(GONE); + tvRefresh.setText("完成"); + } + + @Override + public void onReset() { + rotated = false; + ivSuccess.setVisibility(GONE); + ivArrow.clearAnimation(); + ivArrow.setVisibility(GONE); + progressBar.setVisibility(GONE); + } +} diff --git a/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/LoadMoreFooterView.java b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/LoadMoreFooterView.java new file mode 100644 index 0000000..5ae3044 --- /dev/null +++ b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/LoadMoreFooterView.java @@ -0,0 +1,105 @@ +package com.lvr.recyclerview_helper.sample; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import com.lvr.recyclerview_helper.R; + + +public class LoadMoreFooterView extends FrameLayout { + + private Status mStatus; + + private View mLoadingView; + + private View mErrorView; + + private View mTheEndView; + + private OnRetryListener mOnRetryListener; + + public LoadMoreFooterView(Context context) { + this(context, null); + } + + public LoadMoreFooterView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LoadMoreFooterView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + LayoutInflater.from(context).inflate(R.layout.layout_hrecyclerview_load_more_footer_view, this, true); + + mLoadingView = findViewById(R.id.loadingView); + mErrorView = findViewById(R.id.errorView); + mTheEndView = findViewById(R.id.theEndView); + + mErrorView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mOnRetryListener != null) { + mOnRetryListener.onRetry(LoadMoreFooterView.this); + } + } + }); + + setStatus(Status.GONE); + } + + public void setOnRetryListener(OnRetryListener listener) { + this.mOnRetryListener = listener; + } + + public Status getStatus() { + return mStatus; + } + + public void setStatus(Status status) { + this.mStatus = status; + change(); + } + + public boolean canLoadMore() { + return mStatus == Status.GONE || mStatus == Status.ERROR; + } + + private void change() { + switch (mStatus) { + case GONE: + mLoadingView.setVisibility(GONE); + mErrorView.setVisibility(GONE); + mTheEndView.setVisibility(GONE); + break; + + case LOADING: + mLoadingView.setVisibility(VISIBLE); + mErrorView.setVisibility(GONE); + mTheEndView.setVisibility(GONE); + break; + + case ERROR: + mLoadingView.setVisibility(GONE); + mErrorView.setVisibility(VISIBLE); + mTheEndView.setVisibility(GONE); + break; + + case THE_END: + mLoadingView.setVisibility(GONE); + mErrorView.setVisibility(GONE); + mTheEndView.setVisibility(VISIBLE); + break; + } + } + + public enum Status { + GONE, LOADING, ERROR, THE_END + } + + public interface OnRetryListener { + void onRetry(LoadMoreFooterView view); + } + +} diff --git a/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/MainActivity.java b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/MainActivity.java new file mode 100644 index 0000000..13e4625 --- /dev/null +++ b/recyclerview_helper/app/src/main/java/com/lvr/recyclerview_helper/sample/MainActivity.java @@ -0,0 +1,308 @@ +package com.lvr.recyclerview_helper.sample; + +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; + +import com.lvr.library.adapter.CommonAdapter; +import com.lvr.library.adapter.MultiItemCommonAdapter; +import com.lvr.library.anims.adapters.ScaleInAnimationAdapter; +import com.lvr.library.anims.adapters.SlideInBottomAnimationAdapter; +import com.lvr.library.anims.animators.FlipInLeftYAnimator; +import com.lvr.library.anims.animators.LandingAnimator; +import com.lvr.library.holder.BaseViewHolder; +import com.lvr.library.recyclerview.HRecyclerView; +import com.lvr.library.support.MultiItemTypeSupport; +import com.lvr.recyclerview_helper.R; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class MainActivity extends AppCompatActivity { + + private HRecyclerView mRecyclerView; + private Spinner mSpinner; + private Toolbar mToolbar; + private String[] mTypes = {"单类型", "多类型", "单类型+动画", "多类型+动画", "单类型+点击", "多类型+点击","单类型+头/尾","多类型+头/尾"}; + private String[] mStrings = { + "Google", "Hello", "Iron", "Japan", "Coke", "Yahoo", "Sony", "Canon", "Fujitsu", "USA", "Nexus", "LINE", "Haskell", "C++", + "Java", "Go", "Swift", "Objective-c", "Ruby", "PHP", "Bash", "ksh", "C", "Groovy", "Kotlin" + }; + private int[] mImageRes = {R.drawable.image1, R.drawable.image2,R.drawable.image3,R.drawable.image4,R.drawable.image5,R.drawable.image6}; + private int TYPE_HEAD = 0; + private int TYPE_COMMON = 1; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + mSpinner = (Spinner) findViewById(R.id.spinner); + Button button = (Button) findViewById(R.id.btn_next); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(MainActivity.this,AllActivity.class); + startActivity(intent); + } + }); + mRecyclerView = (HRecyclerView) findViewById(R.id.list); + mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); + setSupportActionBar(mToolbar); + mToolbar = (Toolbar) findViewById(R.id.tool_bar); + + ArrayAdapter spinnerAdapter = + new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); + spinnerAdapter.addAll(Arrays.asList(mTypes)); + mSpinner.setAdapter(spinnerAdapter); + mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position == 0) { + setSingleItem(); + } + if (position == 1) { + setMultiItem(); + } + if (position == 2) { + setSingleWithAnimItem(); + } + if (position == 3) { + setMultiWithAnimItem(); + } + if (position == 4) { + setSingleItemWithClick(); + } + if (position == 5) { + setMultiItemWithClick(); + } + //每点击一次就会多加一对头尾布局 + if(position==6){ + setSingleItemWithView(); + } + //每点击一次就会多加一对头尾布局 + if(position==7){ + setMultiItemWithView(); + } + + + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + } + + + + + + /** + * 多类型+头/尾 + */ + private void setMultiItemWithView() { + MultiItemCommonAdapter adapter = multiSetting(); + + View headView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_head,null,false); + View footView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_foot,null,false); + mRecyclerView.addHeaderView(headView); + mRecyclerView.addFooterView(footView); + mRecyclerView.setAdapter(adapter); + + } + + /** + * 单类型+头/尾 + */ + private void setSingleItemWithView() { + CommonAdapter adapter = singleSetting(); + + View headView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_head,null,false); + View footView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_foot,null,false); + mRecyclerView.addHeaderView(headView); + mRecyclerView.addFooterView(footView); + mRecyclerView.setAdapter(adapter); + + } + + /** + * 多类型+点击 + */ + private void setMultiItemWithClick() { + MultiItemCommonAdapter adapter = multiSetting(); + adapter.setOnItemClickListener(new CommonAdapter.OnItemClickListener() { + @Override + public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) { + showMyDialog("点击事件"); + } + }); + adapter.setOnItemLongClickListener(new CommonAdapter.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) { + showMyDialog("长按事件"); + return false; + } + }); + mRecyclerView.setItemAnimator(new FlipInLeftYAnimator()); + SlideInBottomAnimationAdapter scaleAdapter = new SlideInBottomAnimationAdapter(adapter); + scaleAdapter.setFirstOnly(false); + scaleAdapter.setDuration(500); + mRecyclerView.setAdapter(scaleAdapter); + } + + /** + * 单类型+点击 + */ + private void setSingleItemWithClick() { + CommonAdapter adapter = singleSetting(); + adapter.setOnItemClickListener(new CommonAdapter.OnItemClickListener() { + @Override + public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) { + System.out.println("点击"); + showMyDialog("响应点击事件"); + } + }); + adapter.setOnItemLongClickListener(new CommonAdapter.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) { + showMyDialog("响应长按事件"); + return false; + } + }); + mRecyclerView.setItemAnimator(new LandingAnimator()); + ScaleInAnimationAdapter scaleAdapter = new ScaleInAnimationAdapter(adapter); + scaleAdapter.setFirstOnly(false); + scaleAdapter.setDuration(500); + mRecyclerView.setAdapter(scaleAdapter); + + } + + /** + * 多类型+动画 + */ + private void setMultiWithAnimItem() { + MultiItemCommonAdapter adapter = multiSetting(); + mRecyclerView.setItemAnimator(new FlipInLeftYAnimator()); + SlideInBottomAnimationAdapter scaleAdapter = new SlideInBottomAnimationAdapter(adapter); + scaleAdapter.setFirstOnly(false); + scaleAdapter.setDuration(500); + mRecyclerView.setAdapter(scaleAdapter); + } + + /** + * 单类型+动画 + */ + private void setSingleWithAnimItem() { + CommonAdapter adapter = singleSetting(); + mRecyclerView.setItemAnimator(new LandingAnimator()); + ScaleInAnimationAdapter scaleAdapter = new ScaleInAnimationAdapter(adapter); + scaleAdapter.setFirstOnly(false); + scaleAdapter.setDuration(500); + mRecyclerView.setAdapter(scaleAdapter); + } + + + /** + * 多类型 + */ + private void setMultiItem() { + multiSetting(); + } + + /** + * 单类型 + */ + public void setSingleItem() { + singleSetting(); + } + + /** + * 单类型基本设置 + */ + public CommonAdapter singleSetting() { + List mDatas = Arrays.asList(mStrings); + CommonAdapter mAdapter = new CommonAdapter(this, R.layout.item_common, mDatas) { + @Override + public void convert(BaseViewHolder holder, int position) { + holder.setText(R.id.tv_content,mDatas.get(position)); + int number = new Random().nextInt(3); + holder.setImageResource(R.id.iv_content,mImageRes[number]); + } + }; + mRecyclerView.setAdapter(mAdapter); + return mAdapter; + } + + /** + * 多类型基本设置 + */ + public MultiItemCommonAdapter multiSetting() { + MultiItemTypeSupport support = new MultiItemTypeSupport() { + @Override + public int getLayoutId(int itemType) { + if (itemType == TYPE_HEAD) { + return R.layout.item_special; + } else { + return R.layout.item_common; + } + + } + + @Override + public int getItemViewType(int position, String s) { + if (position%3==0&& position>0) { + return TYPE_HEAD; + } else { + return TYPE_COMMON; + } + } + }; + List mDatas = Arrays.asList(mStrings); + MultiItemCommonAdapter mAdapter = new MultiItemCommonAdapter(this, mDatas, support) { + @Override + public void convert(BaseViewHolder holder, int position) { + if (position%3==0&& position>0) { + holder.setImageResource(R.id.iv_head,R.drawable.multi_image); + } else { + holder.setText(R.id.tv_content,mDatas.get(position)); + int number = new Random().nextInt(3)+3; + holder.setImageResource(R.id.iv_content,mImageRes[number]); + } + } + }; + mRecyclerView.setAdapter(mAdapter); + return mAdapter; + } + public void showMyDialog(String message){ + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setTitle("事件类型"); + builder.setMessage(message); + AlertDialog dialog = builder.create(); + builder.setNegativeButton("知道", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + dialog.show(); + } + + + + + +} diff --git a/recyclerview_helper/app/src/main/res/anim/rotate_down.xml b/recyclerview_helper/app/src/main/res/anim/rotate_down.xml new file mode 100644 index 0000000..5277db9 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/anim/rotate_down.xml @@ -0,0 +1,11 @@ + + + diff --git a/recyclerview_helper/app/src/main/res/anim/rotate_up.xml b/recyclerview_helper/app/src/main/res/anim/rotate_up.xml new file mode 100644 index 0000000..edc507f --- /dev/null +++ b/recyclerview_helper/app/src/main/res/anim/rotate_up.xml @@ -0,0 +1,10 @@ + + + diff --git a/recyclerview_helper/app/src/main/res/drawable/image1.jpg b/recyclerview_helper/app/src/main/res/drawable/image1.jpg new file mode 100644 index 0000000..81a15fb Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image1.jpg differ diff --git a/recyclerview_helper/app/src/main/res/drawable/image2.png b/recyclerview_helper/app/src/main/res/drawable/image2.png new file mode 100644 index 0000000..9d833cd Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image2.png differ diff --git a/recyclerview_helper/app/src/main/res/drawable/image3.jpg b/recyclerview_helper/app/src/main/res/drawable/image3.jpg new file mode 100644 index 0000000..9548bc6 Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image3.jpg differ diff --git a/recyclerview_helper/app/src/main/res/drawable/image4.jpg b/recyclerview_helper/app/src/main/res/drawable/image4.jpg new file mode 100644 index 0000000..a5ebe05 Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image4.jpg differ diff --git a/recyclerview_helper/app/src/main/res/drawable/image5.jpg b/recyclerview_helper/app/src/main/res/drawable/image5.jpg new file mode 100644 index 0000000..aa1f3ca Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image5.jpg differ diff --git a/recyclerview_helper/app/src/main/res/drawable/image6.jpg b/recyclerview_helper/app/src/main/res/drawable/image6.jpg new file mode 100644 index 0000000..bb3bb4e Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image6.jpg differ diff --git a/recyclerview_helper/app/src/main/res/drawable/image_icon.jpeg b/recyclerview_helper/app/src/main/res/drawable/image_icon.jpeg new file mode 100644 index 0000000..988ff12 Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/image_icon.jpeg differ diff --git a/recyclerview_helper/app/src/main/res/drawable/multi_image.jpg b/recyclerview_helper/app/src/main/res/drawable/multi_image.jpg new file mode 100644 index 0000000..e73a300 Binary files /dev/null and b/recyclerview_helper/app/src/main/res/drawable/multi_image.jpg differ diff --git a/recyclerview_helper/app/src/main/res/layout/activity_all.xml b/recyclerview_helper/app/src/main/res/layout/activity_all.xml new file mode 100644 index 0000000..fef6f1d --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/activity_all.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/item_common.xml b/recyclerview_helper/app/src/main/res/layout/item_common.xml new file mode 100644 index 0000000..161b65b --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/item_common.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/item_foot.xml b/recyclerview_helper/app/src/main/res/layout/item_foot.xml new file mode 100644 index 0000000..c4f4fa4 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/item_foot.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/item_head.xml b/recyclerview_helper/app/src/main/res/layout/item_head.xml new file mode 100644 index 0000000..e44997c --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/item_head.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/item_special.xml b/recyclerview_helper/app/src/main/res/layout/item_special.xml new file mode 100644 index 0000000..e021082 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/item_special.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_classic_refresh_header_view.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_classic_refresh_header_view.xml new file mode 100644 index 0000000..4a8a084 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_classic_refresh_header_view.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer.xml new file mode 100644 index 0000000..137bb52 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_error_view.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_error_view.xml new file mode 100644 index 0000000..09e109c --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_error_view.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_loading_view.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_loading_view.xml new file mode 100644 index 0000000..101ff01 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_loading_view.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_the_end_view.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_the_end_view.xml new file mode 100644 index 0000000..0dee07b --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_the_end_view.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_view.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_view.xml new file mode 100644 index 0000000..b196e96 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_load_more_footer_view.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_refresh_header.xml b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_refresh_header.xml new file mode 100644 index 0000000..5d389c9 --- /dev/null +++ b/recyclerview_helper/app/src/main/res/layout/layout_hrecyclerview_refresh_header.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/qq_refresh_success.png b/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/qq_refresh_success.png new file mode 100644 index 0000000..0584ace Binary files /dev/null and b/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/qq_refresh_success.png differ diff --git a/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/twitter_pull_arrow.png b/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/twitter_pull_arrow.png new file mode 100644 index 0000000..f7268c9 Binary files /dev/null and b/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/twitter_pull_arrow.png differ diff --git a/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/twitter_pull_arrow_white.png b/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/twitter_pull_arrow_white.png new file mode 100644 index 0000000..515f8f2 Binary files /dev/null and b/recyclerview_helper/app/src/main/res/mipmap-xxhdpi/twitter_pull_arrow_white.png differ diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/adapter/CommonAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/adapter/CommonAdapter.java new file mode 100644 index 0000000..7ac0d83 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/adapter/CommonAdapter.java @@ -0,0 +1,79 @@ +package com.lvr.library.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; + +import com.lvr.library.holder.BaseViewHolder; + +import java.util.List; + + + +/** + * Created by lvr on 2017/5/24. + */ + +public abstract class CommonAdapter extends RecyclerView.Adapter { + protected Context mContext; + protected int mLayoutId; + protected List mDatas; + protected OnItemClickListener mOnItemClickListener; + protected OnItemLongClickListener mOnItemLongClickListener; + + public CommonAdapter(Context context, int layoutId, List datas) + { + mContext = context; + mLayoutId = layoutId; + mDatas = datas; + } + @Override + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final BaseViewHolder viewHolder = new BaseViewHolder(mContext,parent,mLayoutId); + return viewHolder; + } + + @Override + public void onBindViewHolder(final BaseViewHolder holder, final int position) { + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(mOnItemClickListener!=null){ + mOnItemClickListener.onItemClick(v,holder,position); + } + } + }); + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if(mOnItemLongClickListener!=null){ + return mOnItemLongClickListener.onItemLongClick(v,holder,position); + } + return false; + } + }); + convert(holder, position); + } + + public abstract void convert(BaseViewHolder holder, int position); + + @Override + public int getItemCount() { + return mDatas.size(); + } + + public interface OnItemClickListener { + void onItemClick(View view, RecyclerView.ViewHolder holder, int position); + + } + public interface OnItemLongClickListener{ + boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position); + } + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + this.mOnItemClickListener = onItemClickListener; + } + public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { + this.mOnItemLongClickListener = onItemLongClickListener; + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/adapter/MultiItemCommonAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/adapter/MultiItemCommonAdapter.java new file mode 100644 index 0000000..cd785cb --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/adapter/MultiItemCommonAdapter.java @@ -0,0 +1,42 @@ +package com.lvr.library.adapter; + +import android.content.Context; +import android.view.ViewGroup; + +import com.lvr.library.holder.BaseViewHolder; +import com.lvr.library.support.MultiItemTypeSupport; + +import java.util.List; + +/** + * Created by lvr on 2017/5/24. + */ + +public abstract class MultiItemCommonAdapter extends CommonAdapter +{ + protected MultiItemTypeSupport mMultiItemTypeSupport; + + public MultiItemCommonAdapter(Context context, List datas, + MultiItemTypeSupport multiItemTypeSupport) + { + super(context, -1, datas); + mMultiItemTypeSupport = multiItemTypeSupport; + } + + @Override + public int getItemViewType(int position) + { + return mMultiItemTypeSupport.getItemViewType(position, mDatas.get(position)); + } + + @Override + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) + { + int layoutId = mMultiItemTypeSupport.getLayoutId(viewType); + BaseViewHolder holder = new BaseViewHolder(mContext, parent, layoutId); + return holder; + } + + + +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/AlphaInAnimationAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/AlphaInAnimationAdapter.java new file mode 100644 index 0000000..b3bb527 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/AlphaInAnimationAdapter.java @@ -0,0 +1,27 @@ +package com.lvr.library.anims.adapters; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.support.v7.widget.RecyclerView; +import android.view.View; + + + +public class AlphaInAnimationAdapter extends AnimationAdapter { + + private static final float DEFAULT_ALPHA_FROM = 0f; + private final float mFrom; + + public AlphaInAnimationAdapter(RecyclerView.Adapter adapter) { + this(adapter, DEFAULT_ALPHA_FROM); + } + + public AlphaInAnimationAdapter(RecyclerView.Adapter adapter, float from) { + super(adapter); + mFrom = from; + } + + @Override protected Animator[] getAnimators(View view) { + return new Animator[] { ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f) }; + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/AnimationAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/AnimationAdapter.java new file mode 100644 index 0000000..7e4c005 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/AnimationAdapter.java @@ -0,0 +1,93 @@ +package com.lvr.library.anims.adapters; + +import android.animation.Animator; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import com.lvr.library.anims.animators.ViewHelper; + + +public abstract class AnimationAdapter extends RecyclerView.Adapter { + + private RecyclerView.Adapter mAdapter; + private int mDuration = 300; + private Interpolator mInterpolator = new LinearInterpolator(); + private int mLastPosition = -1; + + private boolean isFirstOnly = true; + + public AnimationAdapter(RecyclerView.Adapter adapter) { + mAdapter = adapter; + } + + @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return mAdapter.onCreateViewHolder(parent, viewType); + } + + @Override public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { + super.registerAdapterDataObserver(observer); + mAdapter.registerAdapterDataObserver(observer); + } + + @Override public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) { + super.unregisterAdapterDataObserver(observer); + mAdapter.unregisterAdapterDataObserver(observer); + } + + @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + mAdapter.onBindViewHolder(holder, position); + + int adapterPosition = holder.getAdapterPosition(); + if (!isFirstOnly || adapterPosition > mLastPosition) { + for (Animator anim : getAnimators(holder.itemView)) { + anim.setDuration(mDuration).start(); + anim.setInterpolator(mInterpolator); + } + mLastPosition = adapterPosition; + } else { + ViewHelper.clear(holder.itemView); + } + } + + @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { + mAdapter.onViewRecycled(holder); + super.onViewRecycled(holder); + } + + @Override public int getItemCount() { + return mAdapter.getItemCount(); + } + + public void setDuration(int duration) { + mDuration = duration; + } + + public void setInterpolator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + public void setStartPosition(int start) { + mLastPosition = start; + } + + protected abstract Animator[] getAnimators(View view); + + public void setFirstOnly(boolean firstOnly) { + isFirstOnly = firstOnly; + } + + @Override public int getItemViewType(int position) { + return mAdapter.getItemViewType(position); + } + + public RecyclerView.Adapter getWrappedAdapter() { + return mAdapter; + } + + @Override public long getItemId(int position) { + return mAdapter.getItemId(position); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/ScaleInAnimationAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/ScaleInAnimationAdapter.java new file mode 100644 index 0000000..17ee837 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/ScaleInAnimationAdapter.java @@ -0,0 +1,29 @@ +package com.lvr.library.anims.adapters; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.support.v7.widget.RecyclerView; +import android.view.View; + + + +public class ScaleInAnimationAdapter extends AnimationAdapter { + + private static final float DEFAULT_SCALE_FROM = .5f; + private final float mFrom; + + public ScaleInAnimationAdapter(RecyclerView.Adapter adapter) { + this(adapter, DEFAULT_SCALE_FROM); + } + + public ScaleInAnimationAdapter(RecyclerView.Adapter adapter, float from) { + super(adapter); + mFrom = from; + } + + @Override protected Animator[] getAnimators(View view) { + ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f); + ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f); + return new ObjectAnimator[] { scaleX, scaleY }; + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInBottomAnimationAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInBottomAnimationAdapter.java new file mode 100644 index 0000000..8f9c850 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInBottomAnimationAdapter.java @@ -0,0 +1,35 @@ +package com.lvr.library.anims.adapters; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +public class SlideInBottomAnimationAdapter extends AnimationAdapter { + + public SlideInBottomAnimationAdapter(RecyclerView.Adapter adapter) { + super(adapter); + } + + @Override protected Animator[] getAnimators(View view) { + return new Animator[] { + ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0) + }; + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInLeftAnimationAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInLeftAnimationAdapter.java new file mode 100644 index 0000000..a31c81b --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInLeftAnimationAdapter.java @@ -0,0 +1,21 @@ +package com.lvr.library.anims.adapters; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.support.v7.widget.RecyclerView; +import android.view.View; + + + +public class SlideInLeftAnimationAdapter extends AnimationAdapter { + + public SlideInLeftAnimationAdapter(RecyclerView.Adapter adapter) { + super(adapter); + } + + @Override protected Animator[] getAnimators(View view) { + return new Animator[] { + ObjectAnimator.ofFloat(view, "translationX", -view.getRootView().getWidth(), 0) + }; + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInRightAnimationAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInRightAnimationAdapter.java new file mode 100644 index 0000000..ecea360 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/adapters/SlideInRightAnimationAdapter.java @@ -0,0 +1,21 @@ +package com.lvr.library.anims.adapters; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.support.v7.widget.RecyclerView; +import android.view.View; + + + +public class SlideInRightAnimationAdapter extends AnimationAdapter { + + public SlideInRightAnimationAdapter(RecyclerView.Adapter adapter) { + super(adapter); + } + + @Override protected Animator[] getAnimators(View view) { + return new Animator[] { + ObjectAnimator.ofFloat(view, "translationX", view.getRootView().getWidth(), 0) + }; + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/BaseItemAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/BaseItemAnimator.java new file mode 100644 index 0000000..7018946 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/BaseItemAnimator.java @@ -0,0 +1,708 @@ +package com.lvr.library.anims.animators; +/* + * Copyright (C) 2017 Wasabeef + * Copyright (C) 2014 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * + */ + +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorCompat; +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.lvr.library.anims.animators.holder.AnimateViewHolder; + +import java.util.ArrayList; +import java.util.List; + + +public abstract class BaseItemAnimator extends SimpleItemAnimator { + + private static final boolean DEBUG = false; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + private ArrayList> mAdditionsList = new ArrayList<>(); + private ArrayList> mMovesList = new ArrayList<>(); + private ArrayList> mChangesList = new ArrayList<>(); + + protected ArrayList mAddAnimations = new ArrayList<>(); + private ArrayList mMoveAnimations = new ArrayList<>(); + protected ArrayList mRemoveAnimations = new ArrayList<>(); + private ArrayList mChangeAnimations = new ArrayList<>(); + + protected Interpolator mInterpolator = new DecelerateInterpolator(); + + private static class MoveInfo { + + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, + int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + public BaseItemAnimator() { + super(); + setSupportsChangeAnimations(false); + } + + public void setInterpolator(Interpolator mInterpolator) { + this.mInterpolator = mInterpolator; + } + + @Override public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + doAnimateRemove(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override public void run() { + boolean removed = mMovesList.remove(moves); + if (!removed) { + // already canceled + return; + } + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, moveInfo.toX, + moveInfo.toY); + } + moves.clear(); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override public void run() { + boolean removed = mChangesList.remove(changes); + if (!removed) { + // already canceled + return; + } + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + public void run() { + boolean removed = mAdditionsList.remove(additions); + if (!removed) { + // already canceled + return; + } + for (ViewHolder holder : additions) { + doAnimateAdd(holder); + } + additions.clear(); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + protected void preAnimateRemoveImpl(final ViewHolder holder) { + } + + protected void preAnimateAddImpl(final ViewHolder holder) { + } + + protected abstract void animateRemoveImpl(final ViewHolder holder); + + protected abstract void animateAddImpl(final ViewHolder holder); + + private void preAnimateRemove(final ViewHolder holder) { + ViewHelper.clear(holder.itemView); + + if (holder instanceof AnimateViewHolder) { + ((AnimateViewHolder) holder).preAnimateRemoveImpl(holder); + } else { + preAnimateRemoveImpl(holder); + } + } + + private void preAnimateAdd(final ViewHolder holder) { + ViewHelper.clear(holder.itemView); + + if (holder instanceof AnimateViewHolder) { + ((AnimateViewHolder) holder).preAnimateAddImpl(holder); + } else { + preAnimateAddImpl(holder); + } + } + + private void doAnimateRemove(final ViewHolder holder) { + if (holder instanceof AnimateViewHolder) { + ((AnimateViewHolder) holder).animateRemoveImpl(holder, new DefaultRemoveVpaListener(holder)); + } else { + animateRemoveImpl(holder); + } + + mRemoveAnimations.add(holder); + } + + private void doAnimateAdd(final ViewHolder holder) { + if (holder instanceof AnimateViewHolder) { + ((AnimateViewHolder) holder).animateAddImpl(holder, new DefaultAddVpaListener(holder)); + } else { + animateAddImpl(holder); + } + + mAddAnimations.add(holder); + } + + @Override public boolean animateRemove(final ViewHolder holder) { + endAnimation(holder); + preAnimateRemove(holder); + mPendingRemovals.add(holder); + return true; + } + + protected long getRemoveDelay(final ViewHolder holder) { + return Math.abs(holder.getOldPosition() * getRemoveDuration() / 4); + } + + @Override public boolean animateAdd(final ViewHolder holder) { + endAnimation(holder); + preAnimateAdd(holder); + mPendingAdditions.add(holder); + return true; + } + + protected long getAddDelay(final ViewHolder holder) { + return Math.abs(holder.getAdapterPosition() * getAddDuration() / 4); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + fromX += ViewCompat.getTranslationX(holder.itemView); + fromY += ViewCompat.getTranslationY(holder.itemView); + endAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + ViewCompat.setTranslationX(view, -deltaX); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, -deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + ViewCompat.animate(view).translationX(0); + } + if (deltaY != 0) { + ViewCompat.animate(view).translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + mMoveAnimations.add(holder); + final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view); + animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() { + @Override public void onAnimationStart(View view) { + dispatchMoveStarting(holder); + } + + @Override public void onAnimationCancel(View view) { + if (deltaX != 0) { + ViewCompat.setTranslationX(view, 0); + } + if (deltaY != 0) { + ViewCompat.setTranslationY(view, 0); + } + } + + @Override public void onAnimationEnd(View view) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, + int toX, int toY) { + final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView); + final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView); + final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView); + endAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX); + ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY); + ViewCompat.setAlpha(oldHolder.itemView, prevAlpha); + if (newHolder != null && newHolder.itemView != null) { + // carry over translation values + endAnimation(newHolder); + ViewCompat.setTranslationX(newHolder.itemView, -deltaX); + ViewCompat.setTranslationY(newHolder.itemView, -deltaY); + ViewCompat.setAlpha(newHolder.itemView, 0); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + private void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + mChangeAnimations.add(changeInfo.oldHolder); + final ViewPropertyAnimatorCompat oldViewAnim = + ViewCompat.animate(view).setDuration(getChangeDuration()); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { + @Override public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override public void onAnimationEnd(View view) { + oldViewAnim.setListener(null); + ViewCompat.setAlpha(view, 1); + ViewCompat.setTranslationX(view, 0); + ViewCompat.setTranslationY(view, 0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + mChangeAnimations.add(changeInfo.newHolder); + final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView); + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()). + alpha(1).setListener(new VpaListenerAdapter() { + @Override public void onAnimationStart(View view) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override public void onAnimationEnd(View view) { + newViewAnimation.setListener(null); + ViewCompat.setAlpha(newView, 1); + ViewCompat.setTranslationX(newView, 0); + ViewCompat.setTranslationY(newView, 0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + ViewCompat.setAlpha(item.itemView, 1); + ViewCompat.setTranslationX(item.itemView, 0); + ViewCompat.setTranslationY(item.itemView, 0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + ViewCompat.animate(view).cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + ViewHelper.clear(item.itemView); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + ViewHelper.clear(item.itemView); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + ViewHelper.clear(item.itemView); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException( + "after animation is cancelled, item should not be in " + "mRemoveAnimations list"); + } + + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException( + "after animation is cancelled, item should not be in " + "mAddAnimations list"); + } + + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException( + "after animation is cancelled, item should not be in " + "mChangeAnimations list"); + } + + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException( + "after animation is cancelled, item should not be in " + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + @Override public boolean isRunning() { + return (!mPendingAdditions.isEmpty() || + !mPendingChanges.isEmpty() || + !mPendingMoves.isEmpty() || + !mPendingRemovals.isEmpty() || + !mMoveAnimations.isEmpty() || + !mRemoveAnimations.isEmpty() || + !mAddAnimations.isEmpty() || + !mChangeAnimations.isEmpty() || + !mMovesList.isEmpty() || + !mAdditionsList.isEmpty() || + !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call #dispatchAnimationsFinished() to notify any + * listeners. + */ + private void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + ViewHelper.clear(item.itemView); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + ViewCompat.setTranslationY(view, 0); + ViewCompat.setTranslationX(view, 0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + ViewCompat.setAlpha(view, 1); + dispatchAddFinished(item); + //this check prevent exception when removal already happened during finishing animation + if (j < additions.size()) { + additions.remove(j); + } + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + ViewCompat.animate(viewHolders.get(i).itemView).cancel(); + } + } + + private static class VpaListenerAdapter implements ViewPropertyAnimatorListener { + + @Override public void onAnimationStart(View view) { + } + + @Override public void onAnimationEnd(View view) { + } + + @Override public void onAnimationCancel(View view) { + } + } + + protected class DefaultAddVpaListener extends VpaListenerAdapter { + + ViewHolder mViewHolder; + + public DefaultAddVpaListener(final ViewHolder holder) { + mViewHolder = holder; + } + + @Override public void onAnimationStart(View view) { + dispatchAddStarting(mViewHolder); + } + + @Override public void onAnimationCancel(View view) { + ViewHelper.clear(view); + } + + @Override public void onAnimationEnd(View view) { + ViewHelper.clear(view); + dispatchAddFinished(mViewHolder); + mAddAnimations.remove(mViewHolder); + dispatchFinishedWhenDone(); + } + } + + protected class DefaultRemoveVpaListener extends VpaListenerAdapter { + + ViewHolder mViewHolder; + + public DefaultRemoveVpaListener(final ViewHolder holder) { + mViewHolder = holder; + } + + @Override public void onAnimationStart(View view) { + dispatchRemoveStarting(mViewHolder); + } + + @Override public void onAnimationCancel(View view) { + ViewHelper.clear(view); + } + + @Override public void onAnimationEnd(View view) { + ViewHelper.clear(view); + dispatchRemoveFinished(mViewHolder); + mRemoveAnimations.remove(mViewHolder); + dispatchFinishedWhenDone(); + } + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInAnimator.java new file mode 100644 index 0000000..fd7310e --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInAnimator.java @@ -0,0 +1,55 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FadeInAnimator extends BaseItemAnimator { + + public FadeInAnimator() { + } + + public FadeInAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInDownAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInDownAnimator.java new file mode 100644 index 0000000..a04d45e --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInDownAnimator.java @@ -0,0 +1,58 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FadeInDownAnimator extends BaseItemAnimator { + + public FadeInDownAnimator() { + } + + public FadeInDownAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(-holder.itemView.getHeight() * .25f) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationY(holder.itemView, -holder.itemView.getHeight() * .25f); + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(0) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInLeftAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInLeftAnimator.java new file mode 100644 index 0000000..3b4f53a --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInLeftAnimator.java @@ -0,0 +1,58 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FadeInLeftAnimator extends BaseItemAnimator { + + public FadeInLeftAnimator() { + } + + public FadeInLeftAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(-holder.itemView.getRootView().getWidth() * .25f) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationX(holder.itemView, -holder.itemView.getRootView().getWidth() * .25f); + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(0) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInRightAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInRightAnimator.java new file mode 100644 index 0000000..04d825f --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInRightAnimator.java @@ -0,0 +1,58 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FadeInRightAnimator extends BaseItemAnimator { + + public FadeInRightAnimator() { + } + + public FadeInRightAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(holder.itemView.getRootView().getWidth() * .25f) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationX(holder.itemView, holder.itemView.getRootView().getWidth() * .25f); + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(0) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInUpAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInUpAnimator.java new file mode 100644 index 0000000..9f9d074 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FadeInUpAnimator.java @@ -0,0 +1,58 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FadeInUpAnimator extends BaseItemAnimator { + + public FadeInUpAnimator() { + } + + public FadeInUpAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(holder.itemView.getHeight() * .25f) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationY(holder.itemView, holder.itemView.getHeight() * .25f); + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(0) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInBottomXAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInBottomXAnimator.java new file mode 100644 index 0000000..c9c744b --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInBottomXAnimator.java @@ -0,0 +1,55 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FlipInBottomXAnimator extends BaseItemAnimator { + + public FlipInBottomXAnimator() { + } + + public FlipInBottomXAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationX(-90) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setRotationX(holder.itemView, -90); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationX(0) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInLeftYAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInLeftYAnimator.java new file mode 100644 index 0000000..aa8f40b --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInLeftYAnimator.java @@ -0,0 +1,55 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FlipInLeftYAnimator extends BaseItemAnimator { + + public FlipInLeftYAnimator() { + } + + public FlipInLeftYAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationY(90) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setRotationY(holder.itemView, 90); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationY(0) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInRightYAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInRightYAnimator.java new file mode 100644 index 0000000..060c372 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInRightYAnimator.java @@ -0,0 +1,55 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FlipInRightYAnimator extends BaseItemAnimator { + + public FlipInRightYAnimator() { + } + + public FlipInRightYAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationY(-90) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setRotationY(holder.itemView, -90); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationY(0) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInTopXAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInTopXAnimator.java new file mode 100644 index 0000000..5b35245 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/FlipInTopXAnimator.java @@ -0,0 +1,55 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class FlipInTopXAnimator extends BaseItemAnimator { + + public FlipInTopXAnimator() { + } + + public FlipInTopXAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationX(90) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setRotationX(holder.itemView, 90); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .rotationX(0) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/LandingAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/LandingAnimator.java new file mode 100644 index 0000000..3b7405c --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/LandingAnimator.java @@ -0,0 +1,61 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class LandingAnimator extends BaseItemAnimator { + + public LandingAnimator() { + } + + public LandingAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .alpha(0) + .scaleX(1.5f) + .scaleY(1.5f) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setAlpha(holder.itemView, 0); + ViewCompat.setScaleX(holder.itemView, 1.5f); + ViewCompat.setScaleY(holder.itemView, 1.5f); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .alpha(1) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/OvershootInLeftAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/OvershootInLeftAnimator.java new file mode 100644 index 0000000..602ba05 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/OvershootInLeftAnimator.java @@ -0,0 +1,58 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.OvershootInterpolator; + +public class OvershootInLeftAnimator extends BaseItemAnimator { + + private final float mTension; + + public OvershootInLeftAnimator() { + mTension = 2.0f; + } + + public OvershootInLeftAnimator(float mTension) { + this.mTension = mTension; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(-holder.itemView.getRootView().getWidth()) + .setDuration(getRemoveDuration()) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationX(holder.itemView, -holder.itemView.getRootView().getWidth()); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(0) + .setDuration(getAddDuration()) + .setListener(new DefaultAddVpaListener(holder)) + .setInterpolator(new OvershootInterpolator(mTension)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} + diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/OvershootInRightAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/OvershootInRightAnimator.java new file mode 100644 index 0000000..8ca573b --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/OvershootInRightAnimator.java @@ -0,0 +1,57 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.OvershootInterpolator; + +public class OvershootInRightAnimator extends BaseItemAnimator { + + private final float mTension; + + public OvershootInRightAnimator() { + mTension = 2.0f; + } + + public OvershootInRightAnimator(float mTension) { + this.mTension = mTension; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(holder.itemView.getRootView().getWidth()) + .setDuration(getRemoveDuration()) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationX(holder.itemView, holder.itemView.getRootView().getWidth()); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(0) + .setDuration(getAddDuration()) + .setInterpolator(new OvershootInterpolator(mTension)) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInAnimator.java new file mode 100644 index 0000000..7fa5048 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInAnimator.java @@ -0,0 +1,58 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class ScaleInAnimator extends BaseItemAnimator { + + public ScaleInAnimator() { + } + + public ScaleInAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(0) + .scaleY(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setScaleX(holder.itemView, 0); + ViewCompat.setScaleY(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInBottomAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInBottomAnimator.java new file mode 100644 index 0000000..eff42fb --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInBottomAnimator.java @@ -0,0 +1,67 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class ScaleInBottomAnimator extends BaseItemAnimator { + + public ScaleInBottomAnimator() { + } + + public ScaleInBottomAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) { + // @TODO https://code.google.com/p/android/issues/detail?id=80863 + // ViewCompat.setPivotY(holder.itemView, holder.itemView.getHeight()); + holder.itemView.setPivotY(holder.itemView.getHeight()); + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(0) + .scaleY(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + // @TODO https://code.google.com/p/android/issues/detail?id=80863 + // ViewCompat.setPivotY(holder.itemView, holder.itemView.getHeight()); + holder.itemView.setPivotY(holder.itemView.getHeight()); + ViewCompat.setScaleX(holder.itemView, 0); + ViewCompat.setScaleY(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInLeftAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInLeftAnimator.java new file mode 100644 index 0000000..aeba1cd --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInLeftAnimator.java @@ -0,0 +1,63 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class ScaleInLeftAnimator extends BaseItemAnimator { + + public ScaleInLeftAnimator() { + } + + public ScaleInLeftAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setPivotX(holder.itemView, 0); + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(0) + .scaleY(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setPivotX(holder.itemView, 0); + ViewCompat.setScaleX(holder.itemView, 0); + ViewCompat.setScaleY(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInRightAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInRightAnimator.java new file mode 100644 index 0000000..9376a84 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInRightAnimator.java @@ -0,0 +1,63 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class ScaleInRightAnimator extends BaseItemAnimator { + + public ScaleInRightAnimator() { + } + + public ScaleInRightAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setPivotX(holder.itemView, holder.itemView.getWidth()); + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(0) + .scaleY(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setPivotX(holder.itemView, holder.itemView.getWidth()); + ViewCompat.setScaleX(holder.itemView, 0); + ViewCompat.setScaleY(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInTopAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInTopAnimator.java new file mode 100644 index 0000000..57d1c41 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ScaleInTopAnimator.java @@ -0,0 +1,67 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class ScaleInTopAnimator extends BaseItemAnimator { + + public ScaleInTopAnimator() { + } + + public ScaleInTopAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) { + // @TODO https://code.google.com/p/android/issues/detail?id=80863 + // ViewCompat.setPivotY(holder.itemView, 0); + holder.itemView.setPivotY(0); + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(0) + .scaleY(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + // @TODO https://code.google.com/p/android/issues/detail?id=80863 + // ViewCompat.setPivotY(holder.itemView, 0); + holder.itemView.setPivotY(0); + ViewCompat.setScaleX(holder.itemView, 0); + ViewCompat.setScaleY(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInDownAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInDownAnimator.java new file mode 100644 index 0000000..c74945e --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInDownAnimator.java @@ -0,0 +1,59 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class SlideInDownAnimator extends BaseItemAnimator { + + public SlideInDownAnimator() { + + } + + public SlideInDownAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(-holder.itemView.getHeight()) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationY(holder.itemView, -holder.itemView.getHeight()); + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(0) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInLeftAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInLeftAnimator.java new file mode 100644 index 0000000..e65ddbb --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInLeftAnimator.java @@ -0,0 +1,56 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class SlideInLeftAnimator extends BaseItemAnimator { + + public SlideInLeftAnimator() { + + } + + public SlideInLeftAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(-holder.itemView.getRootView().getWidth()) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationX(holder.itemView, -holder.itemView.getRootView().getWidth()); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(0) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInRightAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInRightAnimator.java new file mode 100644 index 0000000..eb09ff1 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInRightAnimator.java @@ -0,0 +1,56 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class SlideInRightAnimator extends BaseItemAnimator { + + public SlideInRightAnimator() { + + } + + public SlideInRightAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(holder.itemView.getRootView().getWidth()) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationX(holder.itemView, holder.itemView.getRootView().getWidth()); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationX(0) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInUpAnimator.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInUpAnimator.java new file mode 100644 index 0000000..e2dab17 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/SlideInUpAnimator.java @@ -0,0 +1,59 @@ +package com.lvr.library.anims.animators; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +public class SlideInUpAnimator extends BaseItemAnimator { + + public SlideInUpAnimator() { + + } + + public SlideInUpAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(holder.itemView.getHeight()) + .alpha(0) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .setStartDelay(getRemoveDelay(holder)) + .start(); + } + + @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) { + ViewCompat.setTranslationY(holder.itemView, holder.itemView.getHeight()); + ViewCompat.setAlpha(holder.itemView, 0); + } + + @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) { + ViewCompat.animate(holder.itemView) + .translationY(0) + .alpha(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .setStartDelay(getAddDelay(holder)) + .start(); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ViewHelper.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ViewHelper.java new file mode 100644 index 0000000..ebe1f70 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/ViewHelper.java @@ -0,0 +1,37 @@ +package com.lvr.library.anims.animators; + +import android.support.v4.view.ViewCompat; +import android.view.View; + +/** + * Copyright (C) 2017 Wasabeef + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +public final class ViewHelper { + + public static void clear(View v) { + ViewCompat.setAlpha(v, 1); + ViewCompat.setScaleY(v, 1); + ViewCompat.setScaleX(v, 1); + ViewCompat.setTranslationY(v, 0); + ViewCompat.setTranslationX(v, 0); + ViewCompat.setRotation(v, 0); + ViewCompat.setRotationY(v, 0); + ViewCompat.setRotationX(v, 0); + ViewCompat.setPivotY(v, v.getMeasuredHeight() / 2); + ViewCompat.setPivotX(v, v.getMeasuredWidth() / 2); + ViewCompat.animate(v).setInterpolator(null).setStartDelay(0); + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/holder/AnimateViewHolder.java b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/holder/AnimateViewHolder.java new file mode 100644 index 0000000..ecdc535 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/anims/animators/holder/AnimateViewHolder.java @@ -0,0 +1,16 @@ +package com.lvr.library.anims.animators.holder; + +import android.support.v4.view.ViewPropertyAnimatorListener; +import android.support.v7.widget.RecyclerView; + +public interface AnimateViewHolder { + + void preAnimateAddImpl(final RecyclerView.ViewHolder holder); + + void preAnimateRemoveImpl(final RecyclerView.ViewHolder holder); + + void animateAddImpl(final RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener); + + void animateRemoveImpl(final RecyclerView.ViewHolder holder, + ViewPropertyAnimatorListener listener); +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/holder/BaseViewHolder.java b/recyclerview_helper/library/src/main/java/com/lvr/library/holder/BaseViewHolder.java new file mode 100644 index 0000000..836b71c --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/holder/BaseViewHolder.java @@ -0,0 +1,102 @@ +package com.lvr.library.holder; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.support.annotation.IdRes; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.RecyclerView; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +/** + * 基础的ViewHolder + * ViewHolder只作View的缓存,避免每次findViewById,从而提升运行的效率 + */ +public class BaseViewHolder extends RecyclerView.ViewHolder { + private SparseArray viewArray; + private View mItemView; + + /** + * 构造ViewHolder + * + * @param parent 父类容器 + * @param resId 布局资源文件id + */ + public BaseViewHolder(Context context, ViewGroup parent, @LayoutRes int resId) { + super(LayoutInflater.from(context).inflate(resId, parent, false)); + viewArray = new SparseArray<>(); + } + + /** + * 获取ItemView + * @return ItemView + */ + public View getItemView(){ + return this.itemView; + } + + /** + * 获取布局中的View + * + * @param viewId view的Id + * @param View的类型 + * @return view + */ + public T getView(@IdRes int viewId) { + View view = viewArray.get(viewId); + if (view == null) { + view = itemView.findViewById(viewId); + viewArray.put(viewId, view); + } + return (T) view; + } + + /** + * 给TextView设置内容 + * + * @param viewId TextView的id + * @param text 字符串内容 + */ + public void setText(int viewId, String text) { + TextView tv = getView(viewId); + tv.setText(text); + } + + /** + * 给ImageView设置图片 + * + * @param viewId ImageView的id + * @param drawableId 图片资源 + */ + public void setImageResource(int viewId, int drawableId) { + ImageView iv = getView(viewId); + iv.setImageResource(drawableId); + } + + /** + * 给ImageView设置图片 + * + * @param viewId ImageView的id + * @param bitmap 图片资源 + */ + public void setImageBitmap(int viewId, Bitmap bitmap) { + ImageView iv = getView(viewId); + iv.setImageBitmap(bitmap); + } + /** + * 给ImageView设置图片 + * + * @param viewId ImageView的id + * @param drawable 图片资源 + */ + public void setImageBitmap(int viewId, Drawable drawable) { + ImageView iv = getView(viewId); + iv.setImageDrawable(drawable); + } + +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/HRecyclerView.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/HRecyclerView.java new file mode 100644 index 0000000..bcdb05a --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/HRecyclerView.java @@ -0,0 +1,727 @@ +package com.lvr.library.recyclerview; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.support.v4.view.MotionEventCompat; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import com.lvr.library.R; + + +/** + * Created by lvr on 2017/5/25. + */ + +public class HRecyclerView extends RecyclerView { + private static final String TAG = HRecyclerView.class.getSimpleName(); + + private static final int STATUS_DEFAULT = 0; + + private static final int STATUS_SWIPING_TO_REFRESH = 1; + + private static final int STATUS_RELEASE_TO_REFRESH = 2; + + private static final int STATUS_REFRESHING = 3; + + private static final boolean DEBUG = false; + + private int mStatus; + + private boolean mIsAutoRefreshing; + + private boolean mRefreshEnabled; + + private boolean mLoadMoreEnabled; + + private int mRefreshFinalMoveOffset; + + private OnRefreshListener mOnRefreshListener; + + private OnLoadMoreListener mOnLoadMoreListener; + + private RefreshHeaderLayout mRefreshHeaderContainer; + + private FrameLayout mLoadMoreFooterContainer; + + private LinearLayout mHeaderViewContainer; + + private LinearLayout mFooterViewContainer; + + private View mRefreshHeaderView; + + private View mLoadMoreFooterView; + + private Adapter mOriginAdapter; + + public HRecyclerView(Context context) { + this(context, null); + } + + public HRecyclerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public HRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HRecyclerView, defStyle, 0); + @LayoutRes int refreshHeaderLayoutRes = -1; + @LayoutRes int loadMoreFooterLayoutRes = -1; + int refreshFinalMoveOffset = -1; + boolean refreshEnabled; + boolean loadMoreEnabled; + + try { + refreshEnabled = a.getBoolean(R.styleable.HRecyclerView_refreshEnabled, false); + loadMoreEnabled = a.getBoolean(R.styleable.HRecyclerView_loadMoreEnabled, false); + refreshHeaderLayoutRes = a.getResourceId(R.styleable.HRecyclerView_refreshHeaderLayout, -1); + loadMoreFooterLayoutRes = a.getResourceId(R.styleable.HRecyclerView_loadMoreFooterLayout, -1); + refreshFinalMoveOffset = a.getDimensionPixelOffset(R.styleable.HRecyclerView_refreshFinalMoveOffset, -1); + } finally { + a.recycle(); + } + + setRefreshEnabled(refreshEnabled); + + setLoadMoreEnabled(loadMoreEnabled); + + if (refreshHeaderLayoutRes != -1) { + setRefreshHeaderView(refreshHeaderLayoutRes); + } + if (loadMoreFooterLayoutRes != -1) { + setLoadMoreFooterView(loadMoreFooterLayoutRes); + } + if (refreshFinalMoveOffset != -1) { + setRefreshFinalMoveOffset(refreshFinalMoveOffset); + } + setStatus(STATUS_DEFAULT); + } + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + super.onMeasure(widthSpec, heightSpec); + if (mRefreshHeaderView != null) { + if (mRefreshHeaderView.getMeasuredHeight() > mRefreshFinalMoveOffset) { + mRefreshFinalMoveOffset = 0; + } + } + } + + public void setRefreshEnabled(boolean enabled) { + this.mRefreshEnabled = enabled; + } + + public void setLoadMoreEnabled(boolean enabled) { + this.mLoadMoreEnabled = enabled; + if (mLoadMoreEnabled) { + removeOnScrollListener(mOnLoadMoreScrollListener); + addOnScrollListener(mOnLoadMoreScrollListener); + } else { + removeOnScrollListener(mOnLoadMoreScrollListener); + } + } + + public void setOnRefreshListener(OnRefreshListener listener) { + this.mOnRefreshListener = listener; + } + + public void setOnLoadMoreListener(OnLoadMoreListener listener) { + this.mOnLoadMoreListener = listener; + } + + public void setRefreshing(boolean refreshing) { + if (mStatus == STATUS_DEFAULT && refreshing) { + this.mIsAutoRefreshing = true; + setStatus(STATUS_SWIPING_TO_REFRESH); + startScrollDefaultStatusToRefreshingStatus(); + } else if (mStatus == STATUS_REFRESHING && !refreshing) { + this.mIsAutoRefreshing = false; + startScrollRefreshingStatusToDefaultStatus(); + + } else { + this.mIsAutoRefreshing = false; + Log.w(TAG, "isRefresh = " + refreshing + " current status = " + mStatus); + } + } + + public void setRefreshFinalMoveOffset(int refreshFinalMoveOffset) { + this.mRefreshFinalMoveOffset = refreshFinalMoveOffset; + } + + public void setRefreshHeaderView(View refreshHeaderView) { + if (!(refreshHeaderView instanceof RefreshTrigger)) { + throw new ClassCastException("Refresh header view must be an implement of RefreshTrigger"); + } + + if (mRefreshHeaderView != null) { + removeRefreshHeaderView(); + } + if (mRefreshHeaderView != refreshHeaderView) { + this.mRefreshHeaderView = refreshHeaderView; + ensureRefreshHeaderContainer(); + mRefreshHeaderContainer.addView(refreshHeaderView); + } + } + + public void setRefreshHeaderView(@LayoutRes int refreshHeaderLayoutRes) { + ensureRefreshHeaderContainer(); + final View refreshHeader = LayoutInflater.from(getContext()).inflate(refreshHeaderLayoutRes, mRefreshHeaderContainer, false); + if (refreshHeader != null) { + setRefreshHeaderView(refreshHeader); + } + } + + public void setLoadMoreFooterView(View loadMoreFooterView) { + if (mLoadMoreFooterView != null) { + removeLoadMoreFooterView(); + } + if (mLoadMoreFooterView != loadMoreFooterView) { + this.mLoadMoreFooterView = loadMoreFooterView; + ensureLoadMoreFooterContainer(); + mLoadMoreFooterContainer.addView(loadMoreFooterView); + } + } + + public void setLoadMoreFooterView(@LayoutRes int loadMoreFooterLayoutRes) { + ensureLoadMoreFooterContainer(); + final View loadMoreFooter = LayoutInflater.from(getContext()).inflate(loadMoreFooterLayoutRes, mLoadMoreFooterContainer, false); + if (loadMoreFooter != null) { + setLoadMoreFooterView(loadMoreFooter); + } + } + + public View getRefreshHeaderView() { + return mRefreshHeaderView; + } + + public View getLoadMoreFooterView() { + return mLoadMoreFooterView; + } + + public LinearLayout getHeaderContainer() { + ensureHeaderViewContainer(); + return mHeaderViewContainer; + } + + public LinearLayout getFooterContainer() { + ensureFooterViewContainer(); + return mFooterViewContainer; + } + + public void addHeaderView(View headerView) { + ensureHeaderViewContainer(); + mHeaderViewContainer.addView(headerView); + Adapter adapter = getAdapter(); + if (adapter != null) { + adapter.notifyItemChanged(1); + } + } + + public void addFooterView(View footerView) { + ensureFooterViewContainer(); + mFooterViewContainer.addView(footerView); + Adapter adapter = getAdapter(); + if (adapter != null) { + adapter.notifyItemChanged(adapter.getItemCount() - 2); + } + } + + + @Override + public void setAdapter(Adapter adapter) { + mOriginAdapter =adapter; + ensureRefreshHeaderContainer(); + ensureHeaderViewContainer(); + ensureFooterViewContainer(); + ensureLoadMoreFooterContainer(); + super.setAdapter(new WrapperAdapter(adapter, mRefreshHeaderContainer, mHeaderViewContainer, mFooterViewContainer, mLoadMoreFooterContainer)); + } + + private void ensureRefreshHeaderContainer() { + if (mRefreshHeaderContainer == null) { + mRefreshHeaderContainer = new RefreshHeaderLayout(getContext()); + mRefreshHeaderContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0)); + } + } + + private void ensureLoadMoreFooterContainer() { + if (mLoadMoreFooterContainer == null) { + mLoadMoreFooterContainer = new FrameLayout(getContext()); + mLoadMoreFooterContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + } + + private void ensureHeaderViewContainer() { + if (mHeaderViewContainer == null) { + mHeaderViewContainer = new LinearLayout(getContext()); + mHeaderViewContainer.setOrientation(LinearLayout.VERTICAL); + mHeaderViewContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + } + + private void ensureFooterViewContainer() { + if (mFooterViewContainer == null) { + mFooterViewContainer = new LinearLayout(getContext()); + mFooterViewContainer.setOrientation(LinearLayout.VERTICAL); + mFooterViewContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + } + + private void removeRefreshHeaderView() { + if (mRefreshHeaderContainer != null) { + mRefreshHeaderContainer.removeView(mRefreshHeaderView); + } + } + + private void removeLoadMoreFooterView() { + if (mLoadMoreFooterContainer != null) { + mLoadMoreFooterContainer.removeView(mLoadMoreFooterView); + } + } + + private int mActivePointerId = -1; + private int mLastTouchX = 0; + private int mLastTouchY = 0; + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + final int action = MotionEventCompat.getActionMasked(e); + final int actionIndex = MotionEventCompat.getActionIndex(e); + switch (action) { + case MotionEvent.ACTION_DOWN: { + mActivePointerId = MotionEventCompat.getPointerId(e, 0); + mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); + mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); + } + break; + + case MotionEvent.ACTION_POINTER_DOWN: { + mActivePointerId = MotionEventCompat.getPointerId(e, actionIndex); + mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); + mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); + } + break; + + case MotionEventCompat.ACTION_POINTER_UP: { + onPointerUp(e); + } + break; + } + + return super.onInterceptTouchEvent(e); + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + final int action = MotionEventCompat.getActionMasked(e); + switch (action) { + case MotionEvent.ACTION_DOWN: { + final int index = MotionEventCompat.getActionIndex(e); + mActivePointerId = MotionEventCompat.getPointerId(e, 0); + mLastTouchX = getMotionEventX(e, index); + mLastTouchY = getMotionEventY(e, index); + } + break; + + case MotionEvent.ACTION_MOVE: { + final int index = MotionEventCompat.findPointerIndex(e, mActivePointerId); + if (index < 0) { + Log.e(TAG, "Error processing scroll; pointer index for id " + index + " not found. Did any MotionEvents get skipped?"); + return false; + } + + final int x = getMotionEventX(e, index); + final int y = getMotionEventY(e, index); + + final int dx = x - mLastTouchX; + final int dy = y - mLastTouchY; + + mLastTouchX = x; + mLastTouchY = y; + + final boolean triggerCondition = isEnabled() && mRefreshEnabled && mRefreshHeaderView != null && isFingerDragging() && canTriggerRefresh(); + if (DEBUG) { + Log.i(TAG, "triggerCondition = " + triggerCondition + "; mStatus = " + mStatus + "; dy = " + dy); + } + if (triggerCondition) { + + final int refreshHeaderContainerHeight = mRefreshHeaderContainer.getMeasuredHeight(); + final int refreshHeaderViewHeight = mRefreshHeaderView.getMeasuredHeight(); + + if (dy > 0 && mStatus == STATUS_DEFAULT) { + //下拉 设置成下拉刷新状态 + setStatus(STATUS_SWIPING_TO_REFRESH); + mRefreshTrigger.onStart(false, refreshHeaderViewHeight, mRefreshFinalMoveOffset); + } else if (dy < 0) { + //收缩瞬间 设置成默认状态 + if (mStatus == STATUS_SWIPING_TO_REFRESH && refreshHeaderContainerHeight <= 0) { + setStatus(STATUS_DEFAULT); + } + if (mStatus == STATUS_DEFAULT) { + break; + } + } + + if (mStatus == STATUS_SWIPING_TO_REFRESH || mStatus == STATUS_RELEASE_TO_REFRESH) { + if (refreshHeaderContainerHeight >= refreshHeaderViewHeight) { + setStatus(STATUS_RELEASE_TO_REFRESH); + } else { + setStatus(STATUS_SWIPING_TO_REFRESH); + } + fingerMove(dy); + return true; + } + } + } + break; + + case MotionEventCompat.ACTION_POINTER_DOWN: { + final int index = MotionEventCompat.getActionIndex(e); + mActivePointerId = MotionEventCompat.getPointerId(e, index); + mLastTouchX = getMotionEventX(e, index); + mLastTouchY = getMotionEventY(e, index); + } + break; + + case MotionEventCompat.ACTION_POINTER_UP: { + onPointerUp(e); + } + break; + + case MotionEvent.ACTION_UP: { + onFingerUpStartAnimating(); + } + break; + + case MotionEvent.ACTION_CANCEL: { + onFingerUpStartAnimating(); + } + break; + } + return super.onTouchEvent(e); + } + + private boolean isFingerDragging() { + return getScrollState() == SCROLL_STATE_DRAGGING; + } + + public boolean canTriggerRefresh() { + + if (mOriginAdapter == null || mOriginAdapter.getItemCount() <= 0) { + return true; + } + View firstChild = getChildAt(0); + int position = getChildLayoutPosition(firstChild); + if (position == 0) { + if (firstChild.getTop() == mRefreshHeaderContainer.getTop()) { + return true; + } + } + return false; + } + + private int getMotionEventX(MotionEvent e, int pointerIndex) { + return (int) (MotionEventCompat.getX(e, pointerIndex) + 0.5f); + } + + private int getMotionEventY(MotionEvent e, int pointerIndex) { + return (int) (MotionEventCompat.getY(e, pointerIndex) + 0.5f); + } + + private void onFingerUpStartAnimating() { + if (mStatus == STATUS_RELEASE_TO_REFRESH) { + startScrollReleaseStatusToRefreshingStatus(); + } else if (mStatus == STATUS_SWIPING_TO_REFRESH) { + startScrollSwipingToRefreshStatusToDefaultStatus(); + } + } + + private void onPointerUp(MotionEvent e) { + final int actionIndex = MotionEventCompat.getActionIndex(e); + if (MotionEventCompat.getPointerId(e, actionIndex) == mActivePointerId) { + // Pick a new pointer to pick up the slack. + final int newIndex = actionIndex == 0 ? 1 : 0; + mActivePointerId = MotionEventCompat.getPointerId(e, newIndex); + mLastTouchX = getMotionEventX(e, newIndex); + mLastTouchY = getMotionEventY(e, newIndex); + } + } + + private void fingerMove(int dy) { + int ratioDy = (int) (dy * 0.5f + 0.5f); + int offset = mRefreshHeaderContainer.getMeasuredHeight(); + int finalDragOffset = mRefreshFinalMoveOffset; + + int nextOffset = offset + ratioDy; + if (finalDragOffset > 0) { + if (nextOffset > finalDragOffset) { + ratioDy = finalDragOffset - offset; + } + } + + if (nextOffset < 0) { + ratioDy = -offset; + } + move(ratioDy); + } + + private void move(int dy) { + if (dy != 0) { + int height = mRefreshHeaderContainer.getMeasuredHeight() + dy; + setRefreshHeaderContainerHeight(height); + mRefreshTrigger.onMove(false, false, height); + } + } + + private void setRefreshHeaderContainerHeight(int height) { + mRefreshHeaderContainer.getLayoutParams().height = height; + mRefreshHeaderContainer.requestLayout(); + } + + /** + * 强制刷新的状态 状态是STATUS_SWIPING_TO_REFRESH + */ + private void startScrollDefaultStatusToRefreshingStatus() { + mRefreshTrigger.onStart(true, mRefreshHeaderView.getMeasuredHeight(), mRefreshFinalMoveOffset); + + int targetHeight = mRefreshHeaderView.getMeasuredHeight(); + int currentHeight = mRefreshHeaderContainer.getMeasuredHeight(); + startScrollAnimation(400, new AccelerateInterpolator(), currentHeight, targetHeight); + } + + /** + * 没达到刷新要求,默认恢复 状态是STATUS_SWIPING_TO_REFRESH + */ + private void startScrollSwipingToRefreshStatusToDefaultStatus() { + final int targetHeight = 0; + final int currentHeight = mRefreshHeaderContainer.getMeasuredHeight(); + startScrollAnimation(300, new DecelerateInterpolator(), currentHeight, targetHeight); + } + + /** + * 释放刷新 状态是STATUS_RELEASE_TO_REFRESH + */ + private void startScrollReleaseStatusToRefreshingStatus() { + mRefreshTrigger.onRelease(); + + final int targetHeight = mRefreshHeaderView.getMeasuredHeight(); + final int currentHeight = mRefreshHeaderContainer.getMeasuredHeight(); + startScrollAnimation(300, new DecelerateInterpolator(), currentHeight, targetHeight); + } + + /** + * 强制关闭刷新 状态是STATUS_REFRESHING + */ + private void startScrollRefreshingStatusToDefaultStatus() { + mRefreshTrigger.onComplete(); + + final int targetHeight = 0; + final int currentHeight = mRefreshHeaderContainer.getMeasuredHeight(); + startScrollAnimation(400, new DecelerateInterpolator(), currentHeight, targetHeight); + } + + private ValueAnimator mScrollAnimator; + + private void startScrollAnimation(final int time, final Interpolator interpolator, int value, int toValue) { + if (mScrollAnimator == null) { + mScrollAnimator = new ValueAnimator(); + } + //cancel + mScrollAnimator.removeAllUpdateListeners(); + mScrollAnimator.removeAllListeners(); + mScrollAnimator.cancel(); + + //reset new value + mScrollAnimator.setIntValues(value, toValue); + mScrollAnimator.setDuration(time); + mScrollAnimator.setInterpolator(interpolator); + mScrollAnimator.addUpdateListener(mAnimatorUpdateListener); + mScrollAnimator.addListener(mAnimationListener); + mScrollAnimator.start(); + } + + private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final int height = (Integer) animation.getAnimatedValue(); + setRefreshHeaderContainerHeight(height); + switch (mStatus) { + case STATUS_SWIPING_TO_REFRESH: { + mRefreshTrigger.onMove(false, true, height); + } + break; + + case STATUS_RELEASE_TO_REFRESH: { + mRefreshTrigger.onMove(false, true, height); + } + break; + + case STATUS_REFRESHING: { + mRefreshTrigger.onMove(true, true, height); + } + break; + } + + } + }; + + private Animator.AnimatorListener mAnimationListener = new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + int lastStatus = mStatus; + + switch (mStatus) { + case STATUS_SWIPING_TO_REFRESH: { + //对应强制刷新的状态 + if (mIsAutoRefreshing) { + mRefreshHeaderContainer.getLayoutParams().height = mRefreshHeaderView.getMeasuredHeight(); + mRefreshHeaderContainer.requestLayout(); + setStatus(STATUS_REFRESHING); + if (mOnRefreshListener != null) { + mOnRefreshListener.onRefresh(); + mRefreshTrigger.onRefresh(); + } + } else { + //对应恢复原有情况 + mRefreshHeaderContainer.getLayoutParams().height = 0; + mRefreshHeaderContainer.requestLayout(); + setStatus(STATUS_DEFAULT); + } + } + break; + //对应释放刷新 + case STATUS_RELEASE_TO_REFRESH: { + mRefreshHeaderContainer.getLayoutParams().height = mRefreshHeaderView.getMeasuredHeight(); + mRefreshHeaderContainer.requestLayout(); + setStatus(STATUS_REFRESHING); + if (mOnRefreshListener != null) { + mOnRefreshListener.onRefresh(); + mRefreshTrigger.onRefresh(); + } + } + break; + //对应强制关闭 + case STATUS_REFRESHING: { + mIsAutoRefreshing = false; + mRefreshHeaderContainer.getLayoutParams().height = 0; + mRefreshHeaderContainer.requestLayout(); + setStatus(STATUS_DEFAULT); + mRefreshTrigger.onReset(); + } + break; + } + if (DEBUG) { + Log.i(TAG, "onAnimationEnd " + getStatusLog(lastStatus) + " -> " + getStatusLog(mStatus) + " ;refresh view height:" + mRefreshHeaderContainer.getMeasuredHeight()); + } + } + }; + + private RefreshTrigger mRefreshTrigger = new RefreshTrigger() { + @Override + public void onStart(boolean automatic, int headerHeight, int finalHeight) { + if (mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger) { + RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView; + trigger.onStart(automatic, headerHeight, finalHeight); + } + } + + @Override + public void onMove(boolean finished, boolean automatic, int moved) { + if (mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger) { + RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView; + trigger.onMove(finished, automatic, moved); + } + } + + @Override + public void onRefresh() { + if (mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger) { + RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView; + trigger.onRefresh(); + } + } + + @Override + public void onRelease() { + if (mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger) { + RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView; + trigger.onRelease(); + } + } + + @Override + public void onComplete() { + if (mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger) { + RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView; + trigger.onComplete(); + } + } + + @Override + public void onReset() { + if (mRefreshHeaderView != null && mRefreshHeaderView instanceof RefreshTrigger) { + RefreshTrigger trigger = (RefreshTrigger) mRefreshHeaderView; + trigger.onReset(); + } + } + }; + + private OnLoadMoreScrollListener mOnLoadMoreScrollListener = new OnLoadMoreScrollListener() { + @Override + public void onLoadMore(RecyclerView recyclerView) { + if (mOnLoadMoreListener != null && mStatus == STATUS_DEFAULT) { + mOnLoadMoreListener.onLoadMore(); + } + } + }; + + private void setStatus(int status) { + this.mStatus = status; + if (DEBUG) { + printStatusLog(); + } + } + + private void printStatusLog() { + Log.i(TAG, getStatusLog(mStatus)); + } + + private String getStatusLog(int status) { + final String statusLog; + switch (status) { + case STATUS_DEFAULT: + statusLog = "status_default"; + break; + + case STATUS_SWIPING_TO_REFRESH: + statusLog = "status_swiping_to_refresh"; + break; + + case STATUS_RELEASE_TO_REFRESH: + statusLog = "status_release_to_refresh"; + break; + + case STATUS_REFRESHING: + statusLog = "status_refreshing"; + break; + default: + statusLog = "status_illegal!"; + break; + } + return statusLog; + } +} \ No newline at end of file diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnLoadMoreListener.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnLoadMoreListener.java new file mode 100644 index 0000000..44d467c --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnLoadMoreListener.java @@ -0,0 +1,8 @@ +package com.lvr.library.recyclerview; + + +public interface OnLoadMoreListener { + + void onLoadMore(); + +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnLoadMoreScrollListener.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnLoadMoreScrollListener.java new file mode 100644 index 0000000..40ce577 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnLoadMoreScrollListener.java @@ -0,0 +1,37 @@ +package com.lvr.library.recyclerview; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + + +public abstract class OnLoadMoreScrollListener extends RecyclerView.OnScrollListener { + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + int visibleItemCount = layoutManager.getChildCount(); + + + boolean triggerCondition = visibleItemCount > 0 + && newState == RecyclerView.SCROLL_STATE_IDLE + && canTriggerLoadMore(recyclerView); + + if (triggerCondition) { + onLoadMore(recyclerView); + } + } + + public boolean canTriggerLoadMore(RecyclerView recyclerView) { + View lastChild = recyclerView.getChildAt(recyclerView.getChildCount() - 1); + int position = recyclerView.getChildLayoutPosition(lastChild); + RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + int totalItemCount = layoutManager.getItemCount(); + return totalItemCount - 1 == position; + } + + public abstract void onLoadMore(RecyclerView recyclerView); +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnRefreshListener.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnRefreshListener.java new file mode 100644 index 0000000..67f1c67 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/OnRefreshListener.java @@ -0,0 +1,6 @@ +package com.lvr.library.recyclerview; + + +public interface OnRefreshListener { + void onRefresh(); +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/RefreshHeaderLayout.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/RefreshHeaderLayout.java new file mode 100644 index 0000000..d2c6bd2 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/RefreshHeaderLayout.java @@ -0,0 +1,113 @@ +package com.lvr.library.recyclerview; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by aspsine on 16/3/7. + */ +public class RefreshHeaderLayout extends ViewGroup { + + public RefreshHeaderLayout(Context context) { + this(context, null); + } + + public RefreshHeaderLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RefreshHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + /** + * LayoutParams of RefreshHeaderLayout + */ + public static class LayoutParams extends MarginLayoutParams { + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + } + + /** + * {@inheritDoc} + */ + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + /** + * {@inheritDoc} + */ + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int childCount = getChildCount(); + if (childCount > 0) { + int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + View view = getChildAt(0); + measureChildWithMargins(view, widthMeasureSpec, 0, childHeightSpec, 0); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + layoutChildren(); + } + + private void layoutChildren() { + final int width = getMeasuredWidth(); + final int height = getMeasuredHeight(); + + final int paddingLeft = getPaddingLeft(); + final int paddingTop = getPaddingTop(); + final int paddingRight = getPaddingRight(); + final int paddintBottom = getPaddingBottom(); + + int childCount = getChildCount(); + + if (childCount > 0) { + View child = getChildAt(0); + int childWidth = child.getMeasuredWidth(); + int childHeight = child.getMeasuredHeight(); + + MarginLayoutParams marginLp = (MarginLayoutParams) child.getLayoutParams(); + + int childLeft = paddingLeft + marginLp.leftMargin; + int childTop = paddingTop + marginLp.topMargin - (childHeight - height); + int childRight = childLeft + childWidth; + int childBottom = childTop + childHeight; + + child.layout(childLeft, childTop, childRight, childBottom); + } + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/RefreshTrigger.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/RefreshTrigger.java new file mode 100644 index 0000000..43ade0d --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/RefreshTrigger.java @@ -0,0 +1,17 @@ +package com.lvr.library.recyclerview; + + +public interface RefreshTrigger { + + void onStart(boolean automatic, int headerHeight, int finalHeight); + + void onMove(boolean finished, boolean automatic, int moved); + + void onRefresh(); + + void onRelease(); + + void onComplete(); + + void onReset(); +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/SimpleAnimatorListener.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/SimpleAnimatorListener.java new file mode 100644 index 0000000..63f4ec5 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/SimpleAnimatorListener.java @@ -0,0 +1,25 @@ +package com.lvr.library.recyclerview; + +import android.animation.Animator; + +public class SimpleAnimatorListener implements Animator.AnimatorListener { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/WrapperAdapter.java b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/WrapperAdapter.java new file mode 100644 index 0000000..2e9b365 --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/recyclerview/WrapperAdapter.java @@ -0,0 +1,184 @@ +package com.lvr.library.recyclerview; + +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + + +public class WrapperAdapter extends RecyclerView.Adapter { + protected static final int REFRESH_HEADER = Integer.MIN_VALUE; + protected static final int HEADER = Integer.MIN_VALUE + 1; + protected static final int FOOTER = Integer.MAX_VALUE - 1; + protected static final int LOAD_MORE_FOOTER = Integer.MAX_VALUE; + + private final RecyclerView.Adapter mAdapter; + + private final RefreshHeaderLayout mRefreshHeaderContainer; + + private final FrameLayout mLoadMoreFooterContainer; + + private final LinearLayout mHeaderContainer; + + private final LinearLayout mFooterContainer; + + private RecyclerView.AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + WrapperAdapter.this.notifyDataSetChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + WrapperAdapter.this.notifyItemRangeChanged(positionStart + 2, itemCount); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { + WrapperAdapter.this.notifyItemRangeChanged(positionStart + 2, itemCount, payload); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + WrapperAdapter.this.notifyItemRangeInserted(positionStart + 2, itemCount); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + WrapperAdapter.this.notifyItemRangeRemoved(positionStart + 2, itemCount); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + WrapperAdapter.this.notifyDataSetChanged(); + } + }; + + public WrapperAdapter(RecyclerView.Adapter adapter, RefreshHeaderLayout refreshHeaderContainer, LinearLayout headerContainer, LinearLayout footerContainer, FrameLayout loadMoreFooterContainer) { + this.mAdapter = adapter; + this.mRefreshHeaderContainer = refreshHeaderContainer; + this.mHeaderContainer = headerContainer; + this.mFooterContainer = footerContainer; + this.mLoadMoreFooterContainer = loadMoreFooterContainer; + + mAdapter.registerAdapterDataObserver(mObserver); + } + + public RecyclerView.Adapter getAdapter() { + return mAdapter; + } + + @Override + public void onAttachedToRecyclerView(final RecyclerView recyclerView) { + 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) { + WrapperAdapter wrapperAdapter = (WrapperAdapter) recyclerView.getAdapter(); + if (isFullSpanType(wrapperAdapter.getItemViewType(position))) { + return gridLayoutManager.getSpanCount(); + } else if (spanSizeLookup != null) { + return spanSizeLookup.getSpanSize(position - 2); + } + return 1; + } + }); + } + } + + @Override + public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { + super.onViewAttachedToWindow(holder); + int position = holder.getAdapterPosition(); + int type = getItemViewType(position); + if (isFullSpanType(type)) { + ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams(); + if (layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) { + StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) layoutParams; + lp.setFullSpan(true); + } + } + } + + private boolean isFullSpanType(int type) { + return type == REFRESH_HEADER || type == HEADER || type == FOOTER || type == LOAD_MORE_FOOTER; + } + + @Override + public int getItemCount() { + return mAdapter.getItemCount() + 4; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return REFRESH_HEADER; + } else if (position == 1) { + return HEADER; + } else if (1 < position && position < mAdapter.getItemCount() + 2) { + return mAdapter.getItemViewType(position - 2); + } else if (position == mAdapter.getItemCount() + 2) { + return FOOTER; + } else if (position == mAdapter.getItemCount() + 3) { + return LOAD_MORE_FOOTER; + } + + throw new IllegalArgumentException("Wrong type! Position = " + position); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == REFRESH_HEADER) { + return new RefreshHeaderContainerViewHolder(mRefreshHeaderContainer); + } else if (viewType == HEADER) { + return new HeaderContainerViewHolder(mHeaderContainer); + } else if (viewType == FOOTER) { + return new FooterContainerViewHolder(mFooterContainer); + } else if (viewType == LOAD_MORE_FOOTER) { + return new LoadMoreFooterContainerViewHolder(mLoadMoreFooterContainer); + } else { + return mAdapter.onCreateViewHolder(parent, viewType); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (1 < position && position < mAdapter.getItemCount() + 2) { + mAdapter.onBindViewHolder(holder, position - 2); + } + } + + static class RefreshHeaderContainerViewHolder extends RecyclerView.ViewHolder { + + public RefreshHeaderContainerViewHolder(View itemView) { + super(itemView); + } + } + + static class HeaderContainerViewHolder extends RecyclerView.ViewHolder { + + public HeaderContainerViewHolder(View itemView) { + super(itemView); + } + } + + static class FooterContainerViewHolder extends RecyclerView.ViewHolder { + + public FooterContainerViewHolder(View itemView) { + super(itemView); + } + } + + static class LoadMoreFooterContainerViewHolder extends RecyclerView.ViewHolder { + + public LoadMoreFooterContainerViewHolder(View itemView) { + super(itemView); + } + } +} diff --git a/recyclerview_helper/library/src/main/java/com/lvr/library/support/MultiItemTypeSupport.java b/recyclerview_helper/library/src/main/java/com/lvr/library/support/MultiItemTypeSupport.java new file mode 100644 index 0000000..be98f1c --- /dev/null +++ b/recyclerview_helper/library/src/main/java/com/lvr/library/support/MultiItemTypeSupport.java @@ -0,0 +1,12 @@ +package com.lvr.library.support; + +/** + * Created by lvr on 2017/5/24. + */ + +public interface MultiItemTypeSupport +{ + int getLayoutId(int itemType); + + int getItemViewType(int position, T t); +} diff --git a/recyclerview_helper/library/src/main/res/attrs.xml b/recyclerview_helper/library/src/main/res/attrs.xml new file mode 100644 index 0000000..df25174 --- /dev/null +++ b/recyclerview_helper/library/src/main/res/attrs.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/recyclerview_helper/library/src/main/res/values/attrs.xml b/recyclerview_helper/library/src/main/res/values/attrs.xml new file mode 100644 index 0000000..df25174 --- /dev/null +++ b/recyclerview_helper/library/src/main/res/values/attrs.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file