一种自定义的下拉刷新(RecyclerView也可以使用)
首先上效果图
因为之前参考过很多资料,在使用RecyclerView下拉刷新的时候,网上大多数方法都是在RecyclerView中添加首尾元素达到刷新的目的,但是这样需要写多个xml布局以及修改adapter,感觉使用起来很不方便,于是在研究如何脱离adapter来实现下拉刷新。
有一个难点:需要解决滑动冲突的问题。
解决:
巧妙使用listener的方式,在RecyclerView中定义一个interface listener,然后在layout中获取recyclerview对象,然后实现该listener,同时在滑动时往listener中传入相关数据,然后再layout布局中修改header的LayoutParams就能实现下拉刷新的效果。
详细的我在代码中进行了注解,不明白的可以评论,我回及时回复您,也很欢迎和我讨论。
code:
layout代码,继承自LinearLayout
/**
* author: siney
* Date: 2019/1/17
* description:
*/
public class SlideRefreshingLayout extends LinearLayout {
public SlideRefreshingLayout(Context context) {
super(context);
setClickable(true);
}
public SlideRefreshingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setClickable(true);
}
//提供给外面不适用Recycler的监听器
interface OnScrollListener{
void onScrollChanged(int height, int maxHeight);//此时显示的高度
}
private int yy;
private int refreshingH, nowH;//refreshingH是获取的header的高度, nowH是现在已经滑动了多少高
private View refreshingView;//获取header的View对象
private boolean isSet;//判断布局是否已经设置过
private OnScrollListener listener;//给使用者的回调监听,返回此时Header的高度以及最大高度
private MyRecyclerView recyclerView;//如果子布局中有recyclerview,那么此不为空
private MyRecyclerView.OnTouchScrollListener touchListener = new MyRecyclerView.OnTouchScrollListener() {
//这里为下拉刷新核心代码
//我在Recycler动了些手脚,因为滑动冲突,所以我就是以监听器的方式来传递所需要的滑动数据
@Override
public void touchScrolling(int dist, int action) {
if(recyclerView.isCanScroll() && !recyclerView.canScrollVertically(-1)){
recyclerView.setCanScroll(false);//设置为false说明现在处于refreshing状态,LoadingMore道理一样
}
if(action == MotionEvent.ACTION_UP && !recyclerView.isCanScroll()){
//如果在手离开屏幕后,还是处于false状态,那么会自动关闭refreshing,并且设置为true
bounceClose();
recyclerView.setCanScroll(true);
}else if(action == MotionEvent.ACTION_MOVE && !recyclerView.isCanScroll()){
nowH += dist;
if(nowH > refreshingH){
nowH = refreshingH;
}else if(nowH < 0){
nowH = 0;
recyclerView.setCanScroll(true);
}
if(listener != null)
listener.onScrollChanged(nowH, refreshingH);
resetRefreshingH(nowH);
}
}
};
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(!isSet)
initRefreshingWH();//初始化Layout布局中的所有控件,默认第一个为下拉刷新的布局
}
private void initRefreshingWH() {
isSet = true;
if(getChildCount() > 0){
refreshingView = getChildAt(0);
refreshingH = refreshingView.getMeasuredHeight();
resetRefreshingH(0);//初始化Header为0
for(int i = 0;i < getChildCount();i++){
if(getChildAt(i) instanceof MyRecyclerView){
recyclerView = (MyRecyclerView) getChildAt(i);
recyclerView.setListener(touchListener);
break;
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(recyclerView != null || getChildCount() == 0)
return super.onTouchEvent(event);
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
yy = y;
break;
case MotionEvent.ACTION_MOVE:
//下面进行测量移动的距离
if(y - yy > 0){
//向下移动
nowH += (y - yy);
if(nowH > refreshingH){
nowH = refreshingH;
}
}else if(y - yy < 0){
//向上移动
nowH -= (yy - y);
if(nowH < 0){
nowH = 0;
}
}
yy = y;
resetRefreshingH(nowH);
if(listener != null)
listener.onScrollChanged(nowH, refreshingH);
break;
case MotionEvent.ACTION_UP:
bounceClose();
break;
}
return super.onTouchEvent(event);
}
private void resetRefreshingH(int h){
ViewGroup.LayoutParams lp = refreshingView.getLayoutParams();
lp.height = h;
refreshingView.setLayoutParams(lp);
}
private void bounceClose() {
ValueAnimator animator = ValueAnimator.ofInt(nowH, 0);
animator.setInterpolator(new BounceInterpolator());
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int)animation.getAnimatedValue();
resetRefreshingH(value);
}
});
animator.start();
nowH = 0;
}
public void setListener(OnScrollListener listener) {
this.listener = listener;
}
}
recyclerview代码:
/**
* author: siney
* Date: 2019/1/17
* description:
*/
public class MyRecyclerView extends RecyclerView {
interface OnTouchScrollListener{
void touchScrolling(int dist, int action);
}
public MyRecyclerView(@NonNull Context context) {
super(context);
}
public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
private OnTouchScrollListener listener;//监听moving状态
private int yy;
private boolean canScroll = true;//当前RecyclerView能否滑动
@Override
public boolean onTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();//获取相对屏幕的坐标
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
yy = y;
case MotionEvent.ACTION_MOVE:
listener.touchScrolling(y - yy, MotionEvent.ACTION_MOVE);
yy = y;
if(!canScroll){
return true;//设置为已经消费,这样就不会进行事件传播
}
break;
case MotionEvent.ACTION_UP:
listener.touchScrolling(yy, MotionEvent.ACTION_UP);//松开后如果canScroll是false,那么就会关闭上refresh的页面
break;
}
return super.onTouchEvent(e);
}
public void setListener(OnTouchScrollListener listener) {
this.listener = listener;
}
public boolean isCanScroll() {
return canScroll;
}
public void setCanScroll(boolean canScroll) {
this.canScroll = canScroll;
}
}
xml布局文件
<com.frz.slidefreshlayout.SlideRefreshingLayout
android:id="@+id/layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:layout_centerInParent="true"/>
</RelativeLayout>
<!--<com.frz.slidefreshlayout.MyRecyclerView-->
<!--android:id="@+id/recycler"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"/>-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="asdfasdfsad"
android:background="#BCBCBC"/>
</RelativeLayout>
</com.frz.slidefreshlayout.SlideRefreshingLayout>
Activity
public class MainActivity extends AppCompatActivity {
private TextView text;
private SlideRefreshingLayout layout;
// private RecyclerView recyclerView;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = findViewById(R.id.text);
layout = findViewById(R.id.layout);
// recyclerView = findViewById(R.id.recycler);
initListener();
dataBind();
}
private void dataBind() {
// List<String> list = new ArrayList<>();
// for(int i = 0;i < 20;i++){
// list.add(UUID.randomUUID().toString());
// }
// adapter = new MyAdapter(list);
// LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
// recyclerView.setLayoutManager(manager);
// recyclerView.setAdapter(adapter);
}
private void initListener() {
layout.setListener(new SlideRefreshingLayout.OnScrollListener() {
@Override
public void onScrollChanged(int height, int maxHeight) {
if(height > maxHeight / 2){
text.setText("松手刷新");
}else{
text.setText("继续下拉刷新");
}
}
});
}
}
class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private List<String> list;
public MyAdapter(List<String> list) {
this.list = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
viewHolder.text.setText(list.get(i));
}
@Override
public int getItemCount() {
return list.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView text;
public ViewHolder(@NonNull View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text);
}
}
}
总结:
就是根据layout获取子控件,子控件中存在和不存在recyclerview,都可以判断出来,然后根据需求调整,从而实现下拉刷新。