本文主要介绍使用smartRefreshLayout进行底部的自定义,使用官网上介绍的方法仅仅只能改变文字而无法满足我们的需求,官网中介绍的字符串的替换方法:https://github.com/scwang90/SmartRefreshLayout/blob/master/art/md_faq.md,就是简单的将字符资源替换,如果需要特殊的需求,例如原来的转圈动画太丑了需要替换成自己的动画,那么就要按照官网方法https://github.com/scwang90/SmartRefreshLayout/blob/master/art/md_custom.md自定义footer或header了,较为麻烦,那么有没有简单可行的方案,既不需要完全自定义,又能灵活使用现成的布局完成我们的需求呢?
下图就是具体效果,既不是简单的字符替换,也不需要重新自定义footer那么麻烦:
初始的写法和普通的用法没有什么区别,我们使用经典模式的footer并设置背景颜色:
refreshLayout.setRefreshFooter(new ClassicsFooter(this));
refreshLayout.setPrimaryColorsId(R.color.colorPrimary, android.R.color.white);
既然我们想要改变具体的文字,动画效果,当然需要拿到他们的view进行文字或者动画的修改,通过查看源码或者使用Android studio的Tools下面的layout inspector工具查看布局元素。我们发现,footer内部含有三个元素,他们在InternalClassics.java中进行声名,而且是static全局变量,因此我们的自定义的view就可以拿到布局元素了:
titleView.setId(ID_TEXT_TITLE);
arrowView.setId(ID_IMAGE_ARROW);
progressView.setId(ID_IMAGE_PROGRESS);
centerLayout.setId(android.R.id.widget_frame);
拿到了具体的view,那么后面的事就好办了,我们就可以随心所欲的隐藏、修改现有的三个view,可以利用提供好的接口回调在合适的时机更新我们需要的文字就好了。
例如例子中的逻辑:上拉时文字修改为“正在加载游戏.....”;结束时根据不同情况将文字替换成“请检查网络设置”、“没有更多游戏”、“加载完成”等。
final AtomicBoolean net = new AtomicBoolean(true);
final AtomicInteger mostTimes = new AtomicInteger(0);//假设只有三屏数据
//设置多监听器,包括顶部下拉刷新、底部上滑刷新
refreshLayout.setOnMultiPurposeListener(new SimpleMultiPurposeListener(){
/**
* 根据上拉的状态,设置文字,并且判断条件
* @param refreshLayout
* @param oldState
* @param newState
*/
@Override
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
switch (newState) {
case None:
case PullUpToLoad:
break;
case Loading:
case LoadReleased:
tv.setText("正在加载游戏..."); //在这里修改文字
if (!isNetworkConnected(getApplicationContext())) {
net.set(false);
} else {
net.set(true);
}
break;
case ReleaseToLoad:
tv.setText("release");
break;
case Refreshing:
tv.setText("refreshing");
break;
}
}
/**
* 添加是否可以加载更多数据的条件
* @param refreshLayout
*/
@Override
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
if (mostTimes.get() < 3 && isNetworkConnected(getApplicationContext())) {
mAdapter.loadMore(MoreDatas()); //上滑刷新,数据从下往上添加到界面上
mostTimes.getAndIncrement();
}
refreshLayout.finishLoadMore(1000); //这个记得设置,否则一直转圈
}
/**
* 在这里根据不同的情况来修改加载完成后的提示语
* @param footer
* @param success
*/
@Override
public void onFooterFinish(RefreshFooter footer, boolean success) {
super.onFooterFinish(footer, success);
if (net.get() == false) {
tv.setText("请检查网络设置");
} else if (mostTimes.get() >= 3) {
tv.setText("没有更多游戏");
} else {
tv.setText("加载完成");
if (mostTimes.get() == 2) {
mostTimes.getAndIncrement();
}
}
}
});
}
上面也只是简单的文字替换,当然我们也可以将刷新的那个转圈的imageView给GONE掉,替换成我们自己的动画或者图片。
最重要的是获取footer的根布局:((ViewGroup)mRefreshLayout.getRefreshFooter().getView()).addView(text);
然后里面的text就是我们自己的view了,或者添加上自己需要的动画效果,例如我们在布局中添加一个静态的text:
TextView text = new TextView(mAct);
text.setText("222");
text.setTextColor(Color.argb(255, 255, 0, 0));
text.setTextSize(19);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.RIGHT_OF, ID_TEXT_TITLE);
// lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
// lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
lp.topMargin = tv.getTop();
text.setLayoutParams(lp);
((ViewGroup)mRefreshLayout.getRefreshFooter().getView()).addView(text);
下面的是完整的自定义footer的代码:
package com.example.cm.smart_learn;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.scwang.smartrefresh.header.MaterialHeader;
import com.scwang.smartrefresh.layout.api.RefreshFooter;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.constant.RefreshState;
import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
import com.scwang.smartrefresh.layout.footer.BallPulseFooter;
import com.scwang.smartrefresh.layout.footer.ClassicsFooter;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import com.scwang.smartrefresh.layout.listener.SimpleMultiPurposeListener;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static com.scwang.smartrefresh.layout.internal.InternalClassics.ID_IMAGE_PROGRESS;
import static com.scwang.smartrefresh.layout.internal.InternalClassics.ID_TEXT_TITLE;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
private RefreshLayout refreshLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setHasFixedSize(true);
mAdapter = new MyAdapter(getDatas());
mRecyclerView.setAdapter(mAdapter);
}
private void initView() {
refreshLayout = (RefreshLayout) findViewById(R.id.refreshLayout);
/**
* 设置不同的头部、底部样式
*/
// refreshLayout.setRefreshFooter(new BallPulseFooter(this).setSpinnerStyle(SpinnerStyle.Scale));
refreshLayout.setRefreshFooter(new ClassicsFooter(this));
refreshLayout.setPrimaryColorsId(R.color.colorPrimary, android.R.color.white);
final TextView tv = refreshLayout.getLayout().findViewById(ID_TEXT_TITLE);
final ImageView iv2 = refreshLayout.getLayout().findViewById(ID_IMAGE_PROGRESS);
final AtomicBoolean net = new AtomicBoolean(true);
final AtomicInteger mostTimes = new AtomicInteger(0);//假设只有三屏数据
//设置多监听器,包括顶部下拉刷新、底部上滑刷新
refreshLayout.setOnMultiPurposeListener(new SimpleMultiPurposeListener(){
/**
* 根据上拉的状态,设置文字,并且判断条件
* @param refreshLayout
* @param oldState
* @param newState
*/
@Override
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
switch (newState) {
case None:
case PullUpToLoad:
break;
case Loading:
case LoadReleased:
tv.setText("正在加载游戏..."); //在这里修改文字
if (!isNetworkConnected(getApplicationContext())) {
net.set(false);
} else {
net.set(true);
}
break;
case ReleaseToLoad:
tv.setText("release");
break;
case Refreshing:
tv.setText("refreshing");
break;
}
}
/**
* 添加是否可以加载更多数据的条件
* @param refreshLayout
*/
@Override
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
if (mostTimes.get() < 3 && isNetworkConnected(getApplicationContext())) {
mAdapter.loadMore(MoreDatas()); //上滑刷新,数据从下往上添加到界面上
mostTimes.getAndIncrement();
}
refreshLayout.finishLoadMore(1000); //这个记得设置,否则一直转圈
}
/**
* 在这里根据不同的情况来修改加载完成后的提示语
* @param footer
* @param success
*/
@Override
public void onFooterFinish(RefreshFooter footer, boolean success) {
super.onFooterFinish(footer, success);
if (net.get() == false) {
tv.setText("请检查网络设置");
} else if (mostTimes.get() >= 3) {
tv.setText("没有更多游戏");
} else {
tv.setText("加载完成");
if (mostTimes.get() == 2) {
mostTimes.getAndIncrement();
}
}
}
});
}
//原始的recyclerView数据
private ArrayList getDatas() {
ArrayList data = new ArrayList();
String temp = " item";
for(int i = 0; i < 6; i++) {
data.add(i + temp);
}
return data;
}
//刷新得到的数据
private ArrayList MoreDatas() {
ArrayList data = new ArrayList();
String temp = "新加数据 ";
for(int i = 0; i < 5; i++) {
data.add(temp + i);
}
return data;
}
/**
* 检查网络是否可用
*
* @param context
* @return
*/
public boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
public static class MyAdapter extends RecyclerView.Adapter {
public ArrayList datas = null;
public MyAdapter(ArrayList datas) {
this.datas = datas;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false);
ViewHolder vh = new ViewHolder(view);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(datas.get(position));
}
@Override
public int getItemCount() {
return datas.size();
}
public void loadMore(ArrayList strings) {
datas.addAll(strings);
notifyDataSetChanged();
}
public void refreshData(ArrayList strings) {
datas.addAll(0, strings);
notifyDataSetChanged();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View view){
super(view);
mTextView = (TextView) view.findViewById(R.id.text);
}
}
}
}
总结一下其实也没啥,就是拿到view,通过提供好的各种回调状态去隐藏、修改掉原来的三个元素就好了;如果需要添加新的组件布局,我们动态的new出来添加add进去就好了。
好处就是不需要像官网那样仅仅修改资源文件而不具备灵活性;也不需要像官网介绍那样重新写footer的布局那么麻烦同样具备很高自定义特性。
完整项目的地址:
https://github.com/buder-cp/base_component_learn/commit/681ac43dcb3e95d3a2e9ef3e70ed2c730c98821f