Android基于GridView实现自定义上拉更多和下拉刷新
- 格式:pdf
- 大小:105.99 KB
- 文档页数:9
Android通过代码控制ListView上下滚动的⽅法本⽂将介绍⼀种通过代码控制ListView上下滚动的⽅法。
先上图:按下按钮会触发ListView滚动或停⽌。
实现该功能并不难,下⾯给出主要代码MainActivity.javapackage cn.guet.levide;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ListView;public class MainActivity extends Activity implements OnClickListener {private Button btn_up, btn_down, btn_stop; // 三个按钮private ListView listview;private Adapter adapter;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.main);findBy();init();}private void init() {btn_up.setOnClickListener(this);btn_down.setOnClickListener(this);btn_stop.setOnClickListener(this);adapter = new Adapter(this);listview.setAdapter(adapter);}private void findBy() {btn_up = (Button) findViewById(R.id.btn_scroll_up);btn_down = (Button) findViewById(R.id.btn_scroll_down);btn_stop = (Button) findViewById(R.id.btn_scroll_stop);listview = (ListView) findViewById(R.id.listview);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_scroll_down:listScrollDown();break;case R.id.btn_scroll_up:listScrollUp();break;case R.id.btn_scroll_stop:listScrollOff();break;}}Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {handler.removeCallbacks(run_scroll_down);handler.removeCallbacks(run_scroll_up);}};/*** 向上滚动*/public void listScrollUp() {listScrollOff();handler.postDelayed(run_scroll_up, 0);}/*** 向下滚动*/public void listScrollDown() {listScrollOff();handler.postDelayed(run_scroll_down, 0);}/*** 停⽌滚动*/public void listScrollOff() {handler.removeCallbacks(run_scroll_down);handler.removeCallbacks(run_scroll_up);}Runnable run_scroll_up = new Runnable() {@Overridepublic void run() {/*** public void smoothScrollBy (int distance, int duration)** Added in API level 8 Smoothly scroll by distance pixels over duration milliseconds.** Parameters* distance Distance to scroll in pixels.* duration Duration of the scroll animation in milliseconds.*/listview.smoothScrollBy(1, 10);handler.postDelayed(run_scroll_up, 10);}};Runnable run_scroll_down = new Runnable() {@Overridepublic void run() {listview.smoothScrollBy(-1, 10);handler.postDelayed(run_scroll_down, 10);}};}实现ListView位置变动的是smoothScrollBy⽅法。
(Android)五分钟让你轻松学会下拉刷新和上拉加载更多为了更好的学习和工作,动动小手收藏起来吧!分享一个谷歌自带的下拉刷新和上拉加载更多例子:效果图:自定义RefreshLayout继承 SwipeRefreshLayout/*** 继承自SwipeRefreshLayout,从而实现滑动到底部时上拉加载更多的功能.*/public class RefreshLayout extends SwipeRefreshLayout implementsOnScrollListener {/*** 滑动到最下面时的上拉操作*/private int mT ouchSlop;/*** listview实例*/private ListView mListView;/*** 上拉监听器, 到了最底部的上拉加载操作*/private OnLoadListener mOnLoadListener;/*** ListView的加载中footer*/private View mListViewFooter;/*** 按下时的y坐标*/private int mYDown;/*** 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉*/private int mLastY;/*** 是否在加载中 ( 上拉加载更多 )*/private boolean isLoading = false;/*** @param context*/public RefreshLayout(Context context) {this(context, null);}@SuppressLint("InflateParams")public RefreshLayout(Context context, AttributeSet attrs) {super(context, attrs);mTouchSlop =ViewConfiguration.get(context).getScaledTouchSlop();mListViewFooter = LayoutInflater.from(context).inflate(yout.listview_footer, null, false);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {super.onLayout(changed, left, top, right, bottom);// 初始化ListView对象if (mListView == null) {getListView();}}/*** 获取ListView对象*/private void getListView() {int childs = getChildCount();if (childs > 0) {View childView = getChildAt(0);if (childView instanceof ListView) {mListView = (ListView) childView;// 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载mListView.setOnScrollListener(this);Log.d(VIEW_LOG_TAG, "### 找到listview");}}}/** (non-Javadoc)** @see android.view.ViewGroup#dispatchT ouchEvent(android.view.Moti onEvent)*/@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {final int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:// 按下mYDown = (int) event.getRawY();break;case MotionEvent.ACTION_MOVE:// 移动mLastY = (int) event.getRawY();break;case MotionEvent.ACTION_UP:// 抬起if (canLoad()) {loadData();}break;default:break;}return super.dispatchT ouchEvent(event);}/*** 是否可以加载更多, 条件是到了最底部, listview不在加载中, 且为上拉操作.** @return*/private boolean canLoad() {return isBottom() && !isLoading && isPullUp();}/*** 判断是否到了最底部*/private boolean isBottom() {if (mListView != null && mListView.getAdapter() != null) {return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);}return false;}/*** 是否是上拉操作** @return*/private boolean isPullUp() {return (mYDown - mLastY) >= mT ouchSlop;}/*** 如果到了最底部,而且是上拉操作.那么执行onLoad方法*/private void loadData() {if (mOnLoadListener != null) {// 设置状态setLoading(true);//mOnLoadListener.onLoad();}}/*** @param loading*/public void setLoading(boolean loading) {isLoading = loading;if (isLoading) {mListView.addFooterView(mListViewFooter);} else {mListView.removeFooterView(mListViewFooter);mYDown = 0;mLastY = 0;}}/*** @param loadListener*/public void setOnLoadListener(OnLoadListener loadListener) {mOnLoadListener = loadListener;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {// 滚动时到了最底部也可以加载更多if (canLoad()) {loadData();}}/*** 设置刷新*/public static void setRefreshing(SwipeRefreshLayout refreshLayout,boolean refreshing, boolean notify) {Class<? extends SwipeRefreshLayout> refreshLayoutClass = refreshLayout.getClass();if (refreshLayoutClass != null) {try {Method setRefreshing = refreshLayoutClass.getDeclaredMethod("setRefreshing", boolean.class, boolean.class);setRefreshing.setAccessible(true);setRefreshing.invoke(refreshLayout, refreshing, notify);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}/*** 加载更多的监听器*/public static interface OnLoadListener {public void onLoad();}}public class MainActivity extends Activity implements OnRefreshListener,OnLoadListener {private RefreshLayout swipeLayout;private ListView listView;private MyAdapter adapter;private ArrayList<HashMap<String, String>> list;private View header;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);initView();setData();setListener();/*设置自动刷新 swipeLayout.setRefreshing(true);在 setRefreshing(true)并没有触发onRefresh的,须要手动调用一次*/// swipeLayout.post(new Thread(new Runnable() {// @Override// public void run() {// swipeLayout.setRefreshing(true);// }// }));// onRefresh();}/*** 初始化布局*/@SuppressLint({ "InlinedApi", "InflateParams" })private void initView() {header = getLayoutInflater().inflate(yout.header, null);swipeLayout = (RefreshLayout) findViewById(R.id.swipe_container);swipeLayout.setColorSchemeResources(R.color.color_bule2, R.color.color_bule,R.color.color_bule2,R.color.color_bule3);}/*** 添加数据*/private void setData() {list = new ArrayList<HashMap<String, String>>();for (int i = 0; i < 10; i++) {HashMap<String, String> map = new HashMap<String, String>();map.put("itemImage", i+"默认");map.put("itemText", i+"默认");list.add(map);}listView = (ListView) findViewById(R.id.list);listView.addHeaderView(header);adapter = new MyAdapter(this, list);listView.setAdapter(adapter);}/*** 设置监听*/private void setListener() {swipeLayout.setOnRefreshListener(this);swipeLayout.setOnLoadListener(this);}/*** 上拉刷新*/@Overridepublic void onRefresh() {swipeLayout.postDelayed(new Runnable() {@Overridepublic void run() {// 更新数据更新完后调用该方法结束刷新list.clear();for (int i = 0; i < 8; i++) {HashMap<String, String> map = new HashMap<String, String>();map.put("itemImage", i+"刷新");map.put("itemText", i+"刷新");list.add(map);}adapter.notifyDataSetChanged();swipeLayout.setRefreshing(false);}}, 2000);}/*** 加载更多*/@Overridepublic void onLoad() {swipeLayout.postDelayed(new Runnable() {@Overridepublic void run() {// 更新数据更新完后调用该方法结束刷新swipeLayout.setLoading(false);for (int i = 1; i < 10; i++) {HashMap<String, String> map = new HashMap<String, String>();map.put("itemImage", i+"更多");map.put("itemText", i+"更多");list.add(map);}adapter.notifyDataSetChanged();}}, 2000);}public class MyAdapter extends BaseAdapter {public ArrayList<HashMap<String, String>> list;public Context context;public LayoutInflater layoutInflater;public MyAdapter(Context context, ArrayList<HashMap<String, String>> list) {this.context = context;this.list = list;layoutInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = null;ViewHolder holder = null;if (convertView == null) {view = layoutInflater.inflate(yout.item, null);holder = new ViewHolder();holder.txt =(TextView) view.findViewById(R.id.textView1);view.setTag(holder);} else {view = convertView;holder = (ViewHolder) view.getTag();}holder.txt.setText(list.get(position).get("itemText")); return view;}static class ViewHolder {TextView txt;}}源码下载地址,请回复刷新即可。
Android自定义下拉刷新动画--仿百度外卖下拉刷新动画我们先来看看Android中的动画吧:Android中的动画分为三种:Tween动画,这一类的动画提供了旋转、平移、缩放等效果。
Alpha –淡入淡出Scale –缩放效果Roate –旋转效果Translate –平移效果Frame动画(帧动画),这一类动画可以创建一个Drawable序列,按照指定时间间歇一个一个显示出来。
Property动画(属性动画),Android3.0之后引入出来的属性动画,它更改的是对象的实际属性。
分析我们可以看到百度外卖的下拉刷新的头是一个骑车的快递员在路上疾行,分析一下我们得到下面的动画:背景图片的平移动画太阳的自旋转动画两个小轮子的自旋转动画这就很简单了,接下来我们去百度外面的图片资源文件里找到这几张图片:(下载百度外卖的apk直接解压即可)<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_back1"android:src="@drawable/pull_back"android:layout_width="match_parent"android:layout_height="100dp" /><ImageViewandroid:id="@+id/iv_back2"android:src="@drawable/pull_back"android:layout_width="match_parent"android:layout_height="100dp" /><RelativeLayoutandroid:id="@+id/main"android:layout_centerHorizontal="true"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageViewandroid:layout_marginTop="45dp"android:id="@+id/iv_rider"android:background="@drawable/pull_rider"android:layout_width="50dp"android:layout_height="50dp" /><ImageViewandroid:id="@+id/wheel1"android:layout_marginLeft="10dp"android:layout_marginTop="90dp"android:background="@drawable/pull_wheel"android:layout_width="15dp"android:layout_height="15dp" /><ImageViewandroid:id="@+id/wheel2"android:layout_marginLeft="40dp"android:layout_marginTop="90dp"android:background="@drawable/pull_wheel"android:layout_width="15dp"android:layout_height="15dp" /></RelativeLayout><ImageViewandroid:id="@+id/ivsun"android:layout_marginTop="20dp"android:layout_toRightOf="@+id/main"android:background="@drawable/pull_sun"android:layout_width="30dp"android:layout_height="30dp" /></RelativeLayout>接下来我们定义动画效果:背景图片的平移效果:实现两个animation xml文件,一个起始位置在100%,结束位置在0%,设置repeat属性为循环往复。
Android⾃定义View控件实现刷新效果三种得到LinearInflater的⽅法a. LayoutInflater inflater = getLayoutInflater();b. LayoutInflater localinflater =(LayoutInflater)context.getSystemService(YOUT_INFLATER_SERVICE);c. LayoutInflater inflater = LayoutInflater.from(context);onDraw ⽅法绘图,invalidate刷新界⾯。
效果图:点击⼀下换颜⾊onDraw画完图后,给控件设置点击事件 ,将参数传到控件⾥,然后invalidate刷新1.onDraw画图,并增加changeColor⽅法public class CusView3 extends View {private int color = 0;public CusView3(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint mPaint = new Paint();if (color > 2) {color = 0;}switch (color) {case 0:mPaint.setColor(Color.GREEN);break;case 1:mPaint.setColor(Color.RED);break;case 2:mPaint.setColor(Color.BLUE);break;default:break;}mPaint.setStyle(Style.FILL);mPaint.setTextSize(35.0f);canvas.drawText("点击我刷新", 10, 60, mPaint);}public void changeColor() { //为了让外⾯调⽤color++;}}2.布局<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><xue.test.CusView3android:id="@+id/cusview3"android:layout_width="wrap_content"android:layout_height="wrap_content"></xue.test.CusView3></LinearLayout>3.画图后给控件设置点击事件 ,将参数传到控件⾥,然后invalidate刷新public class TestCustomViewActivity extends Activity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.main);view3 = (CusView3) findViewById(R.id.cusview3);// 点击事件view3.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Message message = new Message();message.what = 1;myHandler.sendMessage(message);}});}Handler myHandler = new Handler() {// 接收到消息后处理public void handleMessage(Message msg) {switch (msg.what) {case 1:// 调⽤⽅法view3.changeColor();// 刷新⽅法view3.invalidate();break;}super.handleMessage(msg);}};private CusView3 view3;}⾄于⾃定义控件占整屏的问题,可能需要⽤layoutparams以上所述是⼩编给⼤家介绍的Android⾃定义View控件实现刷新效果,希望对⼤家有所帮助,如果⼤家有任何疑问欢迎给我留⾔,⼩编会及时回复⼤家的!。
SwipeRefreshLayout+RecyclerView实现上拉刷新和下拉刷新功能SwipeRefreshLayout 是⾕歌公司推出的⽤于下拉刷新的控件,SwipeRefreshLayout已经被放到了sdk中,在Version 19.1之后SwipeRefreshLayout 被放到support v4中。
源码在SDK\sdk\extras\android\support\v4\src\java\android\support\v4\widget\SwipeRefreshLayout.java⾕歌公司只提供了下拉刷新的功能,RecyclerView的出现基本就是为了替代ListView,GridView的。
今天说⼀下最常见的下拉刷新和上拉刷新的功能。
布局⽂件:<android.support.v4.widget.SwipeRefreshLayoutxmlns:android="/apk/res/android"xmlns:tools="/tools"android:id="@+id/swipe_refresh_widget"android:layout_width="match_parent"android:layout_height="match_parent" ><android.support.v7.widget.RecyclerViewandroid:id="@android:id/list"android:layout_width="match_parent"android:layout_height="match_parent"android:cacheColorHint="@null"android:scrollbars="vertical" /></android.support.v4.widget.SwipeRefreshLayout>在Activity中引⽤这个布局并初始化@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mSwipeRefreshWidget = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_widget);mRecyclerView = (RecyclerView) findViewById(android.R.id.list);mSwipeRefreshWidget.setColorScheme(R.color.color1, R.color.color2,R.color.color3, R.color.color4);mSwipeRefreshWidget.setOnRefreshListener(this);// 这句话是为了,第⼀次进⼊页⾯的时候显⽰加载进度条mSwipeRefreshWidget.setProgressViewOffset(false, 0, (int) TypedValue.applyDimension(PLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView,int newState) {super.onScrollStateChanged(recyclerView, newState);if (newState == RecyclerView.SCROLL_STATE_IDLE&& lastVisibleItem + 1 == adapter.getItemCount()) {mSwipeRefreshWidget.setRefreshing(true);// 此处在现实项⽬中,请换成⽹络请求数据代码,sendRequest .....handler.sendEmptyMessageDelayed(0, 3000);}}@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();}});mRecyclerView.setHasFixedSize(true);mLayoutManager = new LinearLayoutManager(this);mRecyclerView.setLayoutManager(mLayoutManager);mRecyclerView.setItemAnimator(new DefaultItemAnimator());adapter = new SampleAdapter();mRecyclerView.setAdapter(adapter);// 此处在现实项⽬中,请换成⽹络请求数据代码,sendRequest .....handler.sendEmptyMessageDelayed(0, 3000);}SwipeRefreshLayout⾥⾯需要注意的Api:1、setOnRefreshListener(OnRefreshListener listener) 设置下拉监听,当⽤户下拉的时候会去执⾏回调2、setColorSchemeColors(int... colors) 设置进度条的颜⾊变化,最多可以设置4种颜⾊3、setProgressViewOffset(boolean scale, int start, int end) 调整进度条距离屏幕顶部的距离4、setRefreshing(boolean refreshing) 设置SwipeRefreshLayout当前是否处于刷新状态,⼀般是在请求数据的时候设置为true,在数据被加载到View中后,设置为false。
Android开发RecyclerView上拉加载更多功能实现实现思维 开始之前先废话⼏句,Android系统没有提供上拉加载的控件,只提供了下拉刷新的SwipeRefreshLayout控件。
这个控件我们就不废话,⽆法实现上拉刷新的功能。
现在我们说说上拉加载更多的功能实现 思维步骤:1. ⾸先我们需要⾃定义重写RecyclerView,这个是重点.原因是,如果不重写RecyclerView会出现ItemView的点击与滑动的时候有事件分发冲突的问题(滑动⽆法得到down的触摸事件,或者点击⽆法得到up的触摸事件),我们重写RecyclerView⽬的就是重新分发事件,将down和up事件不被拦截,拦截我们需要的move事件.2. 创建⼀个叫页尾的布局⽂件,它⽤来在列表的最后⾯显⽰使⽤3. 接着我们需要想办法在RecyclerView.Adapter的适配器⾥导⼊这个页尾布局。
你的列表内容适配器的普通item该如何实现还是如何实现。
4. 为了导⼊这个页尾布局,我们需要在导⼊的List长度+1,因为这个页尾布局是另外加⼊的,需要在getItemCount()这个重写⽅法⾥返回List长度上+1。
5. 现在就需要判断什么时候滚动到了列表的最后,这个时候我们需要重写⼀个之前写RecyclerView.Adapter适配器⼀般不触及的⼀个重写⽅法public int getItemViewType(int position) 。
重写它根据position位置返回普通item和页尾item的ViewType。
6. 能判断什么时候滚动到最后⾯后,我们就需要在写RecyclerView.Adapter适配器⼀样在onCreateViewHolder⽅法⾥导⼊布局,这⾥我们可以根据ViewType判断应该导⼊普通item还是页尾item7. 然后就是处理点击逻辑或者处理刷新逻辑了。
在部分在activity中实现代码部分重写RecyclerView:public class UpLoadingRecyclerView extends RecyclerView {private static final String TAG = "MyRecyclerView";private boolean isScroll;private float downY;private int mHeightPixels;private OnUpScrollListener mOnUpScrollListener;public UpLoadingRecyclerView(@NonNull Context context) {super(context);DisplayMetrics displayMetrics = getResources().getDisplayMetrics();mHeightPixels = displayMetrics.heightPixels;}public UpLoadingRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);DisplayMetrics displayMetrics = getResources().getDisplayMetrics();mHeightPixels = displayMetrics.heightPixels;}public UpLoadingRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);DisplayMetrics displayMetrics = getResources().getDisplayMetrics();mHeightPixels = displayMetrics.heightPixels;}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {return super.dispatchTouchEvent(ev);}/*** 重写拦截的⽬的是只拦截移动事件,不拦截其他触摸事件* @param e* @return*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent e) {super.onInterceptTouchEvent(e);//⼀定要super.onInterceptTouchEvent(e);不要让RecyclerView的其他事件分发受到影响,否则会出现不滚动的问题switch (e.getAction()){case MotionEvent.ACTION_DOWN:isScroll = false;//按下不拦截downY = e.getY();break;case MotionEvent.ACTION_MOVE:if (Math.abs(downY - e.getY()) >= ViewConfiguration.get(getContext()).getScaledTouchSlop()) {//判断是否产⽣了最⼩滑动isScroll = true;//移动了拦截触摸事件} else {isScroll = false;}break;case MotionEvent.ACTION_UP:isScroll = false;//松开不拦截break;}return isScroll;}@Overridepublic boolean onTouchEvent(MotionEvent e) {switch (e.getAction()){case MotionEvent.ACTION_DOWN:return true;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:if (downY-e.getY() > mHeightPixels/3){//判断是不是从下往上滑动了屏幕的三分之⼀if (mOnUpScrollListener!=null) {mOnUpScrollListener.onScroll();Log.e(TAG, "onTouchEvent:滚动了");}}break;}return super.onTouchEvent(e);}public void setOnUpScrollListener(OnUpScrollListener listener){this.mOnUpScrollListener = listener;}public interface OnUpScrollListener{void onScroll();}}页尾布局 pull_up_refresh.xml:<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayoutxmlns:app="/apk/res-auto"xmlns:android="/apk/res/android"android:background="@color/colorGray5"android:layout_width="match_parent"android:layout_height="30dp"><ProgressBarandroid:id="@+id/footer_progress"android:layout_width="14dp"android:layout_height="14dp"android:layout_marginRight="10dp"android:visibility="gone"android:indeterminateDrawable="@anim/pull_up_ic"app:layout_constraintHorizontal_chainStyle="packed"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="@id/footer_text"app:layout_constraintBottom_toBottomOf="@id/footer_text"app:layout_constraintRight_toLeftOf="@id/footer_text"/><TextViewandroid:id="@+id/footer_text"android:text="@string/pull_up_load_more"android:textColor="@color/fontBlack3"android:textSize="@dimen/font_size_14"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintLeft_toRightOf="@id/footer_progress"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintBottom_toBottomOf="parent"/></androidx.constraintlayout.widget.ConstraintLayout>ProgressBar是刷新加载时候的动画图⽚TextView就是⽂本内容适配器代码public class TNoticeListAdapter extends RecyclerView.Adapter<TNoticeListAdapter.ViewHolder> {private List<TNoticeListBase.Notice> mList;private static final int ITEM_VIEW = 1;private static final int FOOTER_VIEW = 2;public static final int FOOTER_TIPS = 0;//提⽰上拉加载更多public static final int FOOTER_ING = 1;//加载中public static final int FOOTER_ERROR = 2;//⽹络异常public static final int FOOTER_FINISH = 3;//没有更多内容private int footerState = 0;private View mFooterView;private OnFooterClickListener mFooterClickListener;private OnItemClickListener mItemClickListener;private int mScrollPosition;public TNoticeListAdapter(List<TNoticeListBase.Notice> list){this.mList = list;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {if (viewType == ITEM_VIEW){View itemView = LayoutInflater.from(parent.getContext()).inflate(yout.t_notice_item,parent,false);final ViewHolder viewHolder = new ViewHolder(itemView);itemView.setOnClickListener(new View.OnClickListener() { //传出item的点击@Overridepublic void onClick(View v) {if (mItemClickListener!=null){int postion = viewHolder.getAdapterPosition();mItemClickListener.onClick(mList.get(postion).code);}}});return viewHolder;}else {mFooterView = LayoutInflater.from(parent.getContext()).inflate(yout.pull_up_refresh,parent,false);mFooterView.setOnClickListener(new View.OnClickListener() { //传出页尾View的点击@Overridepublic void onClick(View v) {if (getFooterState()==FOOTER_ERROR && mFooterClickListener!=null){ //页尾View只在⽹络异常的时候可以被点击 mFooterClickListener.onClick();}}});return new ViewHolder(mFooterView);}}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {setScrollPosition(position);if (getItemViewType(position) == ITEM_VIEW) {TNoticeListBase.Notice data = mList.get(position);holder.itemTime.setText("今天 "+TimeUtil.getHour24(data.time)+":"+TimeUtil.getMinute(data.time));holder.itemContent.setText("\t\t" + data.content);return;} switch (footerState){case FOOTER_TIPS:holder.footerText.setText(R.string.pull_up_load_more);holder.footerProgress.setVisibility(View.GONE);break;case FOOTER_ING:holder.footerText.setText(R.string.loading_more_for_you);holder.footerProgress.setVisibility(View.VISIBLE);break;case FOOTER_ERROR:holder.footerText.setText(work_exception_click_reload);holder.footerProgress.setVisibility(View.GONE);break;case FOOTER_FINISH:holder.footerText.setText(R.string.Theres_nothing_more); holder.footerProgress.setVisibility(View.GONE);break;default:holder.footerText.setText(R.string.pull_up_load_more);holder.footerProgress.setVisibility(View.GONE);break;}}@Overridepublic int getItemViewType(int position) {//根据itemView的位置返回View的类型是普通还是页尾if (position == getItemCount()-1){return FOOTER_VIEW;}else {return ITEM_VIEW;}}@Overridepublic int getItemCount() {return mList.size()==0?0:mList.size()+1;}/*** 得到页尾状态* @return*/public int getFooterState() {return footerState;}/*** 设置页尾状态* @param footerState*/public void setFooterState(int footerState) {this.footerState = footerState;notifyDataSetChanged();}/*** 设置页尾点击监听* @param listener*/public void setFooterClickListener(OnFooterClickListener listener){ this.mFooterClickListener = listener;}/*** 设置item的点击监听* @param listener*/public void setItemClickListener(OnItemClickListener listener){this.mItemClickListener = listener;}/*** 添加数据⽅法* @param list*/public void addData(List<TNoticeListBase.Notice> list){this.mList = list;}/*** 得到滚动位置* @return*/public int getScrollPosition() {return mScrollPosition;}/*** 设置滚动位置⽤于判断当前RecyclerView有没有滚动到最后* @param position*/private void setScrollPosition(int position) {this.mScrollPosition = position;}public class ViewHolder extends RecyclerView.ViewHolder {TextView itemTime;TextView itemContent;TextView footerText;ProgressBar footerProgress;public ViewHolder(@NonNull View itemView) {super(itemView);if (mFooterView!=null && itemView == mFooterView){footerProgress = (ProgressBar)itemView.findViewById(R.id.footer_progress);footerText = (TextView)itemView.findViewById(R.id.footer_text);}else {itemTime = (TextView) itemView.findViewById(R.id.time);itemContent = (TextView) itemView.findViewById(R.id.content);}}}public interface OnFooterClickListener{void onClick();}public interface OnItemClickListener{void onClick(String code);}}在activity⾥的实现public class TNoticeListActivity extends BaseActivity {private UpLoadingRecyclerView mRecyclerView;private SwipeRefreshLayout mSwitchRefresh;private TNoticeListAdapter mAdapter;private LinearLayoutManager mLinearLayoutManager;private List<TNoticeListBase.Notice> mNoticeList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initData();initClick();}@Overridepublic int getLayout() {return yout.activity_t_notice;}@Overridepublic void initView() {mRecyclerView = (RecyclerView)findViewById(R.id.recyclerview);mLinearLayoutManager = new LinearLayoutManager(this);mRecyclerView.setLayoutManager(mLinearLayoutManager);}/*** 模拟数据*/public void initData(){mNoticeList = new ArrayList<>();for (int i=0;i<2;i++){TNoticeListBase.Notice notice = new TNoticeListBase().new Notice();notice.time = 1547604994000L+i;notice.content = "这是⼀个测试⽤的通知内容这是⼀个测试⽤的通知内容这是⼀个测试⽤的通知内容"+i; notice.code = "dsfa13c1z616v5"+i;mNoticeList.add(notice);}mAdapter = new TNoticeListAdapter(mNoticeList);mRecyclerView.setAdapter(mAdapter);}/*** 添加加载模拟数据*/public void addData(){for (int i=0;i<10;i++){TNoticeListBase.Notice notice = new TNoticeListBase().new Notice();notice.time = 1547604994000L+i;notice.content = "这是⼆个测试⽤的通知内容"+i;notice.code = "dsfa13c1z616v5"+i;mNoticeList.add(notice);}mAdapter.addData(mNoticeList);mAdapter.setFooterState(TNoticeListAdapter.FOOTER_TIPS);}public void initClick(){mRecyclerView.setOnUpScrollListener(new UpLoadingRecyclerView.OnUpScrollListener() {@Overridepublic void onScroll() {if (mAdapter.getItemCount()-1 == mAdapter.getPosition() && mAdapter.getFooterState() == TNoticeListAdapter.FOOTER_TIPS){mAdapter.setFooterState(TNoticeListAdapter.FOOTER_ING);//加载中的逻辑L.e("正在加载中");new Thread(new Runnable() {//模拟数据加载@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}runOnUiThread(new Runnable() {@Overridepublic void run() {addData();}});}}).start();}}});mAdapter.setFooterClickListener(new TNoticeListAdapter.OnFooterClickListener() {@Overridepublic void onClick() {L.e("⽹络异常点击了");}});mAdapter.setItemClickListener(new TNoticeListAdapter.OnItemClickListener() {@Overridepublic void onClick(String code) {L.e("item被点击了");}});mSwitchRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {Toast.makeText(TNoticeListActivity.this, "没有更多数据了", Toast.LENGTH_SHORT).show();mSwitchRefresh.setRefreshing(false);//刷新事件结束}});}}判断数据长度不够,提⽰已经是最新数据的办法,此⽅法可以在Activity⾥创建:private final int PAGE_SIZE = 20;private void downRefresh(List<TNoticeListBase.Notice> noticeList) {mAdapter.addData(noticeList);if (noticeList.size() < PAGE_SIZE) { //如果从服务器上获取的数据长度⼩于发送过去的⼀页长度,说明服务器没有数据了.页尾就显⽰没有更多数据 mAdapter.setFooterState(TNoticeListAdapter.FOOTER_FINISH);} else {mAdapter.setFooterState(TNoticeListAdapter.FOOTER_TIPS);}}。
Android⾃定义控件ScrollView实现上下滑动功能本⽂实例为⼤家分享了Android ScrollView实现上下滑动功能的具体代码,供⼤家参考,具体内容如下package com.example.zhuang;import android.content.Context;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.Scroller;public class MyScrollView extends ViewGroup {private int mScreeHeight;//屏幕⾼度private Scroller mScroller;private int mLastY;private int mStart;private int mEnd;private Context context;public MyScrollView(Context context) {super(context);initView(context);}public MyScrollView(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}public MyScrollView(Context context, AttributeSet attrs,int defStyleAttr) {super(context, attrs, defStyleAttr);initView(context);}private void initView(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//DisplayMetrics 类提供了⼀种关于显⽰的通⽤信息,如显⽰⼤⼩,分辨率和字体。
XRecyclerView实现下拉刷新、滚动到底部加载更多等功能介绍:⼀个实现了下拉刷新,滚动到底部加载更多以及添加header功能的的RecyclerView。
使⽤⽅式和RecyclerView完全⼀致,不需要额外的layout,不需要写特殊的adater。
加载效果内置了AVLoadingIndicatorView上的所有效果,可以根据需要指定。
效果:使⽤:xml<RelativeLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.jcodecraeer.xrecyclerview.XRecyclerViewandroid:id="@+id/recyclerview"android:layout_width="fill_parent"android:layout_height="fill_parent" /></RelativeLayout>2.MainActivitypackage com.example.xrecyclerview;import android.os.Bundle;import android.os.Handler;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import youtInflater;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.ViewGroup;import com.jcodecraeer.xrecyclerview.ProgressStyle;import com.jcodecraeer.xrecyclerview.XRecyclerView;import java.util.ArrayList;public class MainActivity extends AppCompatActivity {private XRecyclerView mRecyclerView;private MyAdapter mAdapter;private ArrayList<String> listData;private int refreshTime = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);mRecyclerView = (XRecyclerView)this.findViewById(R.id.recyclerview);LinearLayoutManager layoutManager = new LinearLayoutManager(this);layoutManager.setOrientation(LinearLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(layoutManager);mRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader);mRecyclerView.setLaodingMoreProgressStyle(ProgressStyle.Pacman);mRecyclerView.setArrowImageView(R.drawable.iconfont_downgrey);View header = LayoutInflater.from(this).inflate(yout.recyclerview_header, (ViewGroup)findViewById(android.R.id.content),false); mRecyclerView.addHeaderView(header);mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {@Overridepublic void onRefresh() {refreshTime ++;new Handler().postDelayed(new Runnable(){public void run() {listData.clear();for(int i = 0; i < 15 ;i++){listData.add("item" + i + "after " + refreshTime + " times of refresh");}mAdapter.notifyDataSetChanged();mRecyclerView.refreshComplete();}}, 3000); //refresh data here}@Overridepublic void onLoadMore() {new Handler().postDelayed(new Runnable(){public void run() {for(int i = 0; i < 15 ;i++){listData.add("item" + (i + listData.size()) );}mAdapter.notifyDataSetChanged();mRecyclerView.loadMoreComplete();}}, 3000);}});listData = new ArrayList<String>();mAdapter = new MyAdapter(listData);for(int i = 0; i < 15 ;i++){listData.add("item" + i);}mRecyclerView.setAdapter(mAdapter);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.menu_main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();//noinspection SimplifiableIfStatementif (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}}3. adapterpackage com.example.xrecyclerview;import android.support.v7.widget.RecyclerView;import youtInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.ArrayList;/*** Created by jianghejie on 15/11/26.*/public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {public ArrayList<String> datas = null;public MyAdapter(ArrayList<String> datas) {this.datas = datas;}//创建新View,被LayoutManager所调⽤@Overridepublic ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {View view = LayoutInflater.from(viewGroup.getContext()).inflate(yout.item,viewGroup,false); ViewHolder vh = new ViewHolder(view);return vh;}//将数据与界⾯进⾏绑定的操作@Overridepublic void onBindViewHolder(ViewHolder viewHolder, int position) {viewHolder.mTextView.setText(datas.get(position));}//获取数据的数量@Overridepublic int getItemCount() {return datas.size();}//⾃定义的ViewHolder,持有每个Item的的所有界⾯元素public static class ViewHolder extends RecyclerView.ViewHolder {public TextView mTextView;public ViewHolder(View view){super(view);mTextView = (TextView) view.findViewById(R.id.text);}}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
【Android培训】Android LRecyclerView 操作案例分享-实现下拉刷新、滑动到底部自动加载一直想抽空写下这个开源项目,但是各种原因没有抽时间,今天还是趁着工作间隙写下了这篇文库,与大家分享。
简介LRecyclerView是支持addHeaderView、addFooterView、下拉刷新、分页加载数据的RecyclerView。
它对RecyclerView 控件进行了拓展,给RecyclerView增加HeaderView、FooterView,并且不需要对你的Adapter做任何修改。
主要功能下拉刷新、滑动到底部自动加载下页数据;可以方便添加Header和Footer;头部下拉样式可以自定义;具备item点击和长按事件。
网络错误加载失败点击Footer重新请求数据;可以动态为FooterView赋予不同状态(加载中、加载失败、滑到最底等)。
感谢如果我比别人看得远些,那是因为我站在巨人们的肩上。
(牛顿)本开源控件是基于HeaderAndFooterRecyclerView 开源项目而来,在原基础上进行了扩充。
GradleStep 1. 在你的根build.gradle文件中增加JitPack仓库依赖。
allprojects {repositories {jcenter()maven { url "https://jitpack.io" }}}Step 2. 在你的model的build.gradle文件中增加LRecyclerView依赖。
compile 'com.github.jdsjlzx:LRecyclerView:1.0.0'使用添加HeaderView、FooterViewmDataAdapter = new DataAdapter(this);mDataAdapter.setData(dataList); mHeaderAndFooterRecyclerViewAdapter = new HeaderAndFooterRecyclerViewAdapter(this, mDataAdapter);mRecyclerView.setAdapter(mHeaderAndFooterRecyclerViewAdapter); mRecyclerView.setLayoutManager(new LinearLayoutManager(this));//add a HeaderViewRecyclerViewUtils.setHeaderView(mRecyclerView, new SampleHeader(this));//add a FooterViewRecyclerViewUtils.setFooterView(mRecyclerView, new SampleFooter(this));注意:mHeaderAndFooterRecyclerViewAdapter = new HeaderAndFooterRecyclerViewAdapter(this, mDataAdapter); HeaderAndFooterRecyclerViewAdapter提供了一些实用的功能,使用者不用关心它的实现,只需构造的时候把自己的mDataAdapter以参数形式传进去即可。
<!--Description:上拉刷新,下拉加载更多是现在最流行的手势操作,但是对于初学者来说,在实现上是有一定难度的,<span style="white-space:pre"> </span>网上很多教程讲的都过于复杂,对于初学者无法起到引导作用,特此写本文,帮助安卓新手入门理解此,还有最为重要的一点:本文只帮助你理解,并不是想你成为代码搬运工!别被那么多代码吓到了,其中很多都是注释,仔细看注释对你理解有很大的帮助Author:Booker LDate:2014-05-16-->一,事先准备:实现该功能,最基本的需要两个东西,一个是OnTouchListener,一个是OnScrollListener,必须得理解这两个东西1,OnTouchListener:用于监测触碰事件,当发生手指碰到屏幕时,以及滑动时,需要用到该Listener来处理相应操作2,OnScrollListener:用于监听ListView的滚动事件(不一定是ListView,应是支持该监听的所有控件),当滚动到第一行或最后一行时需要的判断,其中会有三个参数,后面会讲解道二,Let's begin:首先先自定义两个listener并实现对应的接口,并且在可访问公共变量的类中设置两个变量来判断当前滚动值和状态:1, class TestClass extends Activity{//在可访问公共变量的类中设置两个变量来判断当前滚动值和状态private boolean isFirstRow;//是否滚到第一行private boolean isLastRow;是否滚到最后一行.....}其次写两个自定义类来实现相关接口://滚动监听类2.1, class MyScrollListener implements OnScrollListener{/**当滚动时触发的方法*firstVisiableItem--当前list中能见的第一个item下标,也就是当前被滚到最前面的*visiableItemCount--当前可以看见的item的总数*totalItemcount--总共的item数量*/@Overridepublic void onScroll(AbsListView arg0, int firstVisiableItem, int visiableItemCount, int totalItemcount) {//判断当前是否滚动到第一行(根据前面对参数的解释相信你能够理解)if(firstVisiableItem==0&&totalItemcount>0){isFirstRow=true;}else{//否者不是第一行isFirstRow=false;}//判断是否到了滚动到了最后一行if (firstVisiableItem + visiableItemCount == totalItemcount && totalItemcount > 0) {isLastRow = true;}else{isLastRow=false;}}/**当滚动状态发生变化时触发的方法*scrollState--发生的变化的状态,共有:SCROLL_STATE_IDLE(滚动,并且手指已经离开屏幕),SCROLL_STATE_FLING(手指快速滚动时),SCROLL_STATE_TOUCH_SCROLL(手指滚动,但是手指依然在屏幕上)*/@Overridepublic void onScrollStateChanged(AbsListView arg0, int scrollState) {//这个方法的使用视情况而定,这里不需要用到}}//触摸监听类2.2, class MyTouchListener implements OnTouchListener{/**其中MotionEvent为重要参数,它提供了触摸事件的所有参数和方法*/@Overridepublic boolean onTouch(View v, MotionEvent event) {/*这里需要做的几件事:1,当发生down事件时,记录发生该事件的y坐标2,当发生move事件时,判断移动中的y坐标是否能够达成下拉或上拉事件3,当发生up事件时,如果达成上拉或下拉事件时,触发对应事件/*//为了能够形成视觉上的上拉下托简单效果,需要设置如下变量//用于获取显示屏相关内容DisplayMetrics dm =getResources().getDisplayMetrics();//获取dpi值,以便较为精确的算出移动的像素float density=dm.densityDpi/100;//得到布局参数,用它来实现视觉上的拖动效果youtParams lp = new youtParams(, ;//设置一个变量来记录原始y坐标private float or_y=0f;//event中调用getAction()来获取当前触发的是什么事件,这里我们要用到down,move,up这三个事件switch(event.getAction()){case MotionEvent.ACTION_DOWN://发生down事件记录原始y坐标,getY()方法可重载,用于判断多点触控,这个不需要判断or_y=event.getY();break;case MotionEvent.ACTION_MOVE://当手势移动时,获取当前y坐标,并与之前的坐标进行对比,来判断是往上还是往下//设置变量获取移动值,往上托为正值,往下拉为负值int y_dis=(int)(or_y-event.getY());//判断是否达成拖动条件if(isFirstRow&&y_dis<0){//处于第一行并且继续往下拉动//得到移动距离的绝对值并且算出其移动的相对距离,float distance=Math.abs(y_dis/density/2);lp.topMargin=(int)distance;//形成往下拉的效果,其中listView为对应的控件(不一定是ListView)listView.setLayoutParams(lp);}else if(isLastRow&&y_dis>0){//处于最后一行并且继续网上拉时//同理float distance=Math.abs(y_dis/density/2);lp.bottomMargin=(int)distance;listView.setLayoutParams(lp);}break;case MotionEvent.ACTION_UP://当发生up事件时,如果达成条件可触发事件,即触发对应事件if(isFirstRow){lp.topMargin=0;//调用获取刷新数据的接口方法getNewest();}else if(isLastRow){lp.bottomMargin=0;//调用获取更多数据的接口方法getMore();}listView.setLayoutParams(lp);break;}}}最后在listView中设置对应的监听即可:3, onCreate(){//得到控件listView=(ListView)R.findViewById(;//设置对应的listener即可listView.setOnTouchListener(new MyTouchListener());listView.setOnScrollListener(new MyScrollListener());}DONE~AND ENJOY。
Android ListView下拉刷新、上拉载入更多
0. XListView继承ListView。
1. 下拉刷新组件是ListView的一个Header。
在ListView创建时就将这个自定义View塞进去,默认情况是看不到的,所以这个HeaderView的高度初始设置为0。
2. 上拉载入更多组件是Footer,为了确保这个footer在最后(可能会添加多个自定义footer),在用户调用setAdatper的时候再把这个footer塞进去。
3. 覆写ListView的onTouchEvent方法,处理各种情况。
4. 用户松手,启动mScroller,将header、footer回滚到所需状态。
5. 添加了用户下拉、上拉移动delay的效果,类似iOS的行为。
6. 提供了两个接口:
a) IXListViewListener: 触发下拉刷新、上拉载入更多
b) OnXScrollListener: 这个和原生的OnScrollListener一样,但是在mScroller回滚时,也会触发这里的事件。
AndroidExpandableListView实现下拉刷新和加载更多效果⽀持下拉刷新和加载更多的ExpandableListView,供⼤家参考,具体内容如下模拟器有点卡,滑动的时候⿏标不⽅便怎么⽤:XML中声明<com.xingyi.elonggradletaskdemo.widget.SExpandableListViewandroid:listSelector="@android:color/transparent"android:id="@+id/elv_coupon"android:layout_width="match_parent"android:layout_height="match_parent"android:dividerHeight="1dp"></com.xingyi.elonggradletaskdemo.widget.SExpandableListView>UI中配置下拉刷新的回调以及是否⽀持下拉和加载更多expandableListView = (SExpandableListView)findViewById(R.id.elv_coupon);// 在设置适配器之前设置是否⽀持下拉刷新expandableListView.setLoadingMoreEnabled(true);expandableListView.setPullRefreshEnabled(true);expandableListView.setAdapter(couponAdapter);设置下拉刷新和加载跟多的回调接⼝expandableListView.setmLoadingListener(new SExpandableListView.LoadingListener() {@Overridepublic void onLoadMore() {// 模拟加载更多isPull = false;// 这⾥应该在项⽬中请求,这⾥⽤延时模拟接⼝请求loadCount++;Message msg = handler.obtainMessage();msg.arg1 = loadCount;handler.sendMessageDelayed(msg, 2000);Log.e("TAG---HANDLER:", loadCount + "-->");}@Overridepublic void onRefresh() {isPull = true;loadCount++;// 模拟接⼝请求下拉刷新Message msg = handler.obtainMessage();msg.arg1 = loadCount;handler.sendMessageDelayed(msg, 2000);Log.e("TAG---HANDLER:", loadCount + "-->");}});private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);// 模拟接⼝回复数据,加⼊下拉刷新次数为6次的话我们就认为没有更多了if (loadCount > 6) {expandableListView.refreshComplete();expandableListView.setNoMore(true);} else {addLoadMoreData();if (isPull) {expandableListView.refreshComplete();}couponAdapter.notifyDataSetChanged();// 是不是全部展开,根据需求设置// expanedAll();}Log.e("TAG---收到消息:", loadCount + "-->");}};简单看下实现过程⾃定义ExpandableListview重写onTouch事件给⾃定义的view添加滑动监听事件初始化的时候给View添加header 和 footer ⽤来下拉刷新// 这⾥在⾃定view构造函数中调⽤的增加header和footer的代码,注意下两种添加⽅式的区别,详情见注释和源代码private void initSE(Context context) {/*** 这⾥是footer的填充,注意指定他的⽗亲为当前的listview,* 这⾥footer不⽤指定layoutparem是因为footer 在填充的时候已经指定了他的⽗view*/loadMoreView = LayoutInflater.from(context).inflate(yout.item_footer_view, this, false);loadMorePb = (ProgressBar) loadMoreView.findViewById(R.id.pb_loading);loadMoreDesc = (TextView) loadMoreView.findViewById(_loadmore_desc);}@Overridepublic void setAdapter(ExpandableListAdapter adapter) {if (pullRefreshEnabled) {mRefreshHeader = new ArrowRefreshHeader(getContext());mRefreshHeader.setProgressStyle(ProgressStyle.LineScale);/*** 注意⼀定要指定header的layoutparasm为 AbsListView,以为 header是 new出来的他默认的 layoutparm是当前header的类型,* 如果我们不修改的话会报错类型转化异常,*/mRefreshHeader.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, youtParams.WRAP_CONTENT)); addHeaderView(mRefreshHeader);}if (loadingMoreEnabled) {addFooterView(loadMoreView);}super.setAdapter(adapter);}源代码下载地址:以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
activity_main.xml<RelativeLayoutxmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><me.maxwin.view.XListViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_centerVertical="true"android:id="@+id/xlv"/></RelativeLayout>xlistview_header.xml<?xml version="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="bottom"><RelativeLayoutandroid:id="@+id/xlistview_header_content"android:layout_width="fill_parent"android:layout_height="60dp"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:gravity="center"android:orientation="vertical"android:id="@+id/xlistview_header_text"><TextViewandroid:id="@+id/xlistview_header_hint_textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/xlistview_header_hint_normal"/><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="3dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content" android:text="@string/xlistview_header_last_time"android:textSize="12sp"/><TextViewandroid:id="@+id/xlistview_header_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="12sp"/></LinearLayout></LinearLayout><ImageViewandroid:id="@+id/xlistview_header_arrow"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@id/xlistview_header_text"android:layout_centerVertical="true"android:layout_marginLeft="-35dp"android:src="@drawable/xlistview_arrow"/><ProgressBarandroid:id="@+id/xlistview_header_progressbar"android:layout_width="30dp"android:layout_height="30dp"android:layout_alignLeft="@id/xlistview_header_text"android:layout_centerVertical="true"android:layout_marginLeft="-40dp"android:visibility="invisible"/></RelativeLayout></LinearLayout>xlistview_footer.xml<?xml version="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"><RelativeLayoutandroid:id="@+id/xlistview_footer_content"android:layout_width="fill_parent"android:layout_height="wrap_content"android:padding="10dp"><ProgressBarandroid:id="@+id/xlistview_footer_progressbar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:visibility="invisible"/><TextViewandroid:id="@+id/xlistview_footer_hint_textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="@string/xlistview_footer_hint_normal" /></RelativeLayout></LinearLayout>MainActivitypackage com.example.shuaxin;import java.util.ArrayList;import com.example.adapter.MyAdapter;import com.example.vo.MusicList;import com.example.vo.PersonVo;import com.google.gson.Gson;import com.lidroid.xutils.HttpUtils;import com.lidroid.xutils.exception.HttpException;import com.lidroid.xutils.http.ResponseInfo;import com.lidroid.xutils.http.callback.RequestCallBack;import com.lidroid.xutils.http.client.HttpRequest.HttpMethod; import me.maxwin.view.XListView;import me.maxwin.view.XListView.IXListViewListener;import android.os.Bundle;import android.os.Handler;import android.util.Log;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.Toast;import android.app.Activity;import android.content.Intent;public class MainActivity extends Activity {private int pager=1;private boolean flag=true;private HttpUtils httpUtils;private XListView xlv;private ArrayList<MusicList> list=new ArrayList<MusicList>();private MyAdapter adapter;Handler handler=new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_main);init();}public void init(){httpUtils=new HttpUtils();xlv=(XListView) findViewById(R.id.xlv);//设置能上拉加载xlv.setPullLoadEnable(true);//设置能下拉刷新xlv.setPullRefreshEnable(true);//数据getData();adapter = new MyAdapter(list, this);xlv.setAdapter(adapter);//设置监听xlv.setXListViewListener(new IXListViewListener() {//下拉刷新@Overridepublic void onRefresh() {// TODO Auto-generated method stubhandler.postDelayed(new Runnable() {@Overridepublic void run() {//刷新就是清空原来的数据,加载新数据list.clear();getData();adapter = new MyAdapter(list, MainActivity.this);xlv.setAdapter(adapter);onLoad();}}, 2000);}//上拉加载@Overridepublic void onLoadMore() {// TODO Auto-generated method stubhandler.postDelayed(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubgetData();adapter.notifyDataSetChanged();onLoad();}}, 2000);}});xlv.setOnItemClickListener(new OnItemClickListener() { @Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {// TODO Auto-generated method stubIntent in=new Intent(MainActivity.this, DetilActivity.class);in.putExtra("123",list.get(arg2-1).getMc_imageurl());startActivity(in);}});}//加载数据private void getData() {Stringurl="http://169.254.250.36:8080/Music03/SelectMusic?u_id=&page=10 &page_size="+pager;httpUtils.send(HttpMethod.GET, url, new RequestCallBack<String>() {@Overridepublic void onFailure(HttpException arg0, String arg1) { // TODO Auto-generated method stubLog.i("TAG", "++++++++++++请求失败");}@Overridepublic void onSuccess(ResponseInfo<String> arg0) {// TODO Auto-generated method stubLog.i("TAG", "服务器返回的数据======="+arg0.result);Gson g=new Gson();PersonVo pv=g.fromJson(arg0.result, PersonVo.class);pager++;if(pv.getMusiclist().size()>=10){list.addAll(pv.getMusiclist());adapter.notifyDataSetChanged();}else{if(flag){list.addAll(pv.getMusiclist());adapter.notifyDataSetChanged();flag=false;}else{Toast.makeText(MainActivity.this, "没有更多数据了!", Toast.LENGTH_LONG).show();}}}});}public void onLoad(){xlv.stopLoadMore();xlv.stopRefresh();xlv.setRefreshTime("刚刚");}}//使用webwei显示图片//wv.loadUrl(img_url);//wv.setWebViewClient(new WebViewClient());XListView/*** @file XListView.java* @package me.maxwin.view* @create Mar 18, 2012 6:28:41 PM* @author Maxwin* @description An ListView support (a) Pull down to refresh, (b) Pull up to load more.* Implement IXListViewListener, and see stopRefresh() / stopLoadMore().*/package me.maxwin.view;import com.example.shuaxin.R;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.animation.DecelerateInterpolator;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ListAdapter;import android.widget.ListView;import android.widget.RelativeLayout;import android.widget.Scroller;import android.widget.TextView;public class XListView extends ListView implements OnScrollListener {private float mLastY = -1; // save event yprivate Scroller mScroller; // used for scroll backprivate OnScrollListener mScrollListener; // user's scroll listener// the interface to trigger refresh and load more.private IXListViewListener mListViewListener;// -- header viewprivate XListViewHeader mHeaderView;// header view content, use it to calculate the Header's height. And hide it// when disable pull refresh.private RelativeLayout mHeaderViewContent;private TextView mHeaderTimeView;private int mHeaderViewHeight; // header view's heightprivate boolean mEnablePullRefresh = true;private boolean mPullRefreshing = false; // is refreashing.// -- footer viewprivate XListViewFooter mFooterView;private boolean mEnablePullLoad;private boolean mPullLoading;private boolean mIsFooterReady = false;// total list items, used to detect is at the bottom of listview.private int mTotalItemCount;// for mScroller, scroll back from header or footer.private int mScrollBack;private final static int SCROLLBACK_HEADER = 0;private final static int SCROLLBACK_FOOTER = 1;private final static int SCROLL_DURATION = 400; // scroll back durationprivate final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px// at bottom, trigger// load more.private final static float OFFSET_RADIO = 1.8f; // support iOS like pull// feature./*** @param context*/public XListView(Context context) {super(context);initWithContext(context);}public XListView(Context context, AttributeSet attrs) { super(context, attrs);initWithContext(context);}public XListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initWithContext(context);}private void initWithContext(Context context) {mScroller = new Scroller(context, new DecelerateInterpolator());// XListView need the scroll event, and it will dispatch the event to// user's listener (as a proxy).super.setOnScrollListener(this);// init header viewmHeaderView = new XListViewHeader(context);mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);addHeaderView(mHeaderView);// init footer viewmFooterView = new XListViewFooter(context);// init header heightmHeaderView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {mHeaderViewHeight = mHeaderViewContent.getHeight();getViewTreeObserver().removeGlobalOnLayoutListener(this);}});}@Overridepublic void setAdapter(ListAdapter adapter) {// make sure XListViewFooter is the last footer view, and only add once.if (mIsFooterReady == false) {mIsFooterReady = true;addFooterView(mFooterView);}super.setAdapter(adapter);}/*** enable or disable pull down refresh feature.** @param enable*/public void setPullRefreshEnable(boolean enable) {mEnablePullRefresh = enable;if (!mEnablePullRefresh) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE);} else {mHeaderViewContent.setVisibility(View.VISIBLE);}}/*** enable or disable pull up load more feature.** @param enable*/public void setPullLoadEnable(boolean enable) {mEnablePullLoad = enable;if (!mEnablePullLoad) {mFooterView.hide();mFooterView.setOnClickListener(null);//make sure "pull up" don't show a line in bottom when listview with one pagesetFooterDividersEnabled(false);} else {mPullLoading = false;mFooterView.show();mFooterView.setState(XListViewFooter.STATE_NORMAL);//make sure "pull up" don't show a line in bottom when listview with one pagesetFooterDividersEnabled(true);// both "pull up" and "click" will invoke load more.mFooterView.setOnClickListener(new OnClickListener() { @Overridepublic void onClick(View v) {startLoadMore();}});}}/*** stop refresh, reset header view.*/public void stopRefresh() {if (mPullRefreshing == true) {mPullRefreshing = false;resetHeaderHeight();}}/*** stop load more, reset footer view.*/public void stopLoadMore() {if (mPullLoading == true) {mPullLoading = false;mFooterView.setState(XListViewFooter.STATE_NORMAL);}}/*** set last refresh time** @param time*/public void setRefreshTime(String time) {mHeaderTimeView.setText(time);}private void invokeOnScrolling() {if (mScrollListener instanceof OnXScrollListener) {OnXScrollListener l = (OnXScrollListener) mScrollListener;l.onXScrolling(this);}}private void updateHeaderHeight(float delta) {mHeaderView.setVisiableHeight((int) delta+ mHeaderView.getVisiableHeight());if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {mHeaderView.setState(XListViewHeader.STATE_READY);} else {mHeaderView.setState(XListViewHeader.STATE_NORMAL);}}setSelection(0); // scroll to top each time}/*** reset header view's height.*/private void resetHeaderHeight() {int height = mHeaderView.getVisiableHeight();if (height == 0) // not visible.return;// refreshing and header isn't shown fully. do nothing.if (mPullRefreshing && height <= mHeaderViewHeight) { return;}int finalHeight = 0; // default: scroll back to dismiss header.// is refreshing, just scroll back to show all the header.if (mPullRefreshing && height > mHeaderViewHeight) {finalHeight = mHeaderViewHeight;}mScrollBack = SCROLLBACK_HEADER;mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);// trigger computeScrollinvalidate();}private void updateFooterHeight(float delta) {int height = mFooterView.getBottomMargin() + (int) delta;if (mEnablePullLoad && !mPullLoading) {if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load// more.mFooterView.setState(XListViewFooter.STATE_READY);} else {mFooterView.setState(XListViewFooter.STATE_NORMAL);}}mFooterView.setBottomMargin(height);// setSelection(mTotalItemCount - 1); // scroll to bottom }private void resetFooterHeight() {int bottomMargin = mFooterView.getBottomMargin();if (bottomMargin > 0) {mScrollBack = SCROLLBACK_FOOTER;mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION);invalidate();}}private void startLoadMore() {mPullLoading = true;mFooterView.setState(XListViewFooter.STATE_LOADING);if (mListViewListener != null) {mListViewListener.onLoadMore();}}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (mLastY == -1) {mLastY = ev.getRawY();}switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:mLastY = ev.getRawY();break;case MotionEvent.ACTION_MOVE:final float deltaY = ev.getRawY() - mLastY;mLastY = ev.getRawY();if (getFirstVisiblePosition() == 0&& (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {// the first item is showing, header has shown or pull down.updateHeaderHeight(deltaY / OFFSET_RADIO);invokeOnScrolling();} else if (getLastVisiblePosition() == mTotalItemCount - 1&& (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {// last item, already pulled up or want to pull up.updateFooterHeight(-deltaY / OFFSET_RADIO);}break;default:mLastY = -1; // resetif (getFirstVisiblePosition() == 0) {// invoke refreshif (mEnablePullRefresh&& mHeaderView.getVisiableHeight() > mHeaderViewHeight) {mPullRefreshing = true;mHeaderView.setState(XListViewHeader.STATE_REFRESHING);if (mListViewListener != null) {mListViewListener.onRefresh();}}resetHeaderHeight();} else if (getLastVisiblePosition() == mTotalItemCount - 1) {// invoke load more.if (mEnablePullLoad&& mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA&& !mPullLoading) {startLoadMore();}resetFooterHeight();}break;}return super.onTouchEvent(ev);}@Overridepublic void computeScroll() {if (puteScrollOffset()) {if (mScrollBack == SCROLLBACK_HEADER) {mHeaderView.setVisiableHeight(mScroller.getCurrY());} else {mFooterView.setBottomMargin(mScroller.getCurrY());}postInvalidate();invokeOnScrolling();}puteScroll();}@Overridepublic void setOnScrollListener(OnScrollListener l) { mScrollListener = l;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (mScrollListener != null) {mScrollListener.onScrollStateChanged(view, scrollState);}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {// send to user's listenermTotalItemCount = totalItemCount;if (mScrollListener != null) {mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,totalItemCount);}}public void setXListViewListener(IXListViewListener l) { mListViewListener = l;}/*** you can listen ListView.OnScrollListener or this one. it will invoke* onXScrolling when header/footer scroll back.*/public interface OnXScrollListener extends OnScrollListener { public void onXScrolling(View view);}/*** implements this interface to get refresh/load more event.*/public interface IXListViewListener {public void onRefresh();public void onLoadMore();}}XListViewFooter/*** @file XFooterView.java* @create Mar 31, 2012 9:33:43 PM* @author Maxwin* @description XListView's footer*/package me.maxwin.view;import com.example.shuaxin.R;import android.content.Context;import android.util.AttributeSet;import youtInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;public class XListViewFooter extends LinearLayout { public final static int STATE_NORMAL = 0;public final static int STATE_READY = 1;public final static int STATE_LOADING = 2;private Context mContext;private View mContentView;private View mProgressBar;private TextView mHintView;public XListViewFooter(Context context) {super(context);initView(context);}public XListViewFooter(Context context, AttributeSet attrs) { super(context, attrs);initView(context);}public void setState(int state) {mHintView.setVisibility(View.INVISIBLE);mProgressBar.setVisibility(View.INVISIBLE);mHintView.setVisibility(View.INVISIBLE);if (state == STATE_READY) {mHintView.setVisibility(View.VISIBLE);mHintView.setText(R.string.xlistview_footer_hint_ready);} else if (state == STATE_LOADING) {mProgressBar.setVisibility(View.VISIBLE);} else {mHintView.setVisibility(View.VISIBLE);mHintView.setText(R.string.xlistview_footer_hint_normal);}}public void setBottomMargin(int height) {if (height < 0) return ;youtParams lp = (youtParams)mContentView.getLayoutParams();lp.bottomMargin = height;mContentView.setLayoutParams(lp);}public int getBottomMargin() {youtParams lp = (youtParams)mContentView.getLayoutParams();return lp.bottomMargin;}/*** normal status*/public void normal() {mHintView.setVisibility(View.VISIBLE);mProgressBar.setVisibility(View.GONE);}/*** loading status*/public void loading() {mHintView.setVisibility(View.GONE);mProgressBar.setVisibility(View.VISIBLE);}/*** hide footer when disable pull load more*/public void hide() {youtParams lp = (youtParams)mContentView.getLayoutParams();lp.height = 0;mContentView.setLayoutParams(lp);}/*** show footer*/public void show() {youtParams lp = (youtParams)mContentView.getLayoutParams();lp.height = LayoutParams.WRAP_CONTENT;mContentView.setLayoutParams(lp);}private void initView(Context context) {mContext = context;LinearLayout moreView = (LinearLayout)LayoutInflater.from(mContext).inflate(yout.xlis tview_footer, null);addView(moreView);moreView.setLayoutParams(newyoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));mContentView = moreView.findViewById(R.id.xlistview_footer_content);mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar);mHintView = (TextView)moreView.findViewById(R.id.xlistview_footer_hint_textvi ew);}}XListViewHeader/*** @file XListViewHeader.java* @create Apr 18, 2012 5:22:27 PM* @author Maxwin* @description XListView's header*/package me.maxwin.view;import com.example.shuaxin.R;import android.content.Context;import android.util.AttributeSet;import android.view.Gravity;import youtInflater;import android.view.View;import android.view.animation.Animation;import android.view.animation.RotateAnimation;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.TextView;public class XListViewHeader extends LinearLayout {private LinearLayout mContainer;private ImageView mArrowImageView;private ProgressBar mProgressBar;private TextView mHintTextView;private int mState = STATE_NORMAL;private Animation mRotateUpAnim;private Animation mRotateDownAnim;private final int ROTATE_ANIM_DURATION = 180;public final static int STATE_NORMAL = 0;public final static int STATE_READY = 1;public final static int STATE_REFRESHING = 2;public XListViewHeader(Context context) {super(context);initView(context);}/*** @param context* @param attrs*/public XListViewHeader(Context context, AttributeSet attrs) { super(context, attrs);initView(context);}private void initView(Context context) {// 鍒濆 鎯呭喌锛岃 缃 笅鎷夊埛鏂皏iew楂樺害涓�youtParams lp = new youtParams( LayoutParams.FILL_PARENT, 0);mContainer = (LinearLayout) LayoutInflater.from(context).inflate(yout.xlistview_header, null);addView(mContainer, lp);setGravity(Gravity.BOTTOM);mArrowImageView = (ImageView)findViewById(R.id.xlistview_header_arrow);mHintTextView = (TextView)findViewById(R.id.xlistview_header_hint_textview);mProgressBar = (ProgressBar)findViewById(R.id.xlistview_header_progressbar);mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);mRotateUpAnim.setFillAfter(true);mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);mRotateDownAnim.setFillAfter(true);}public void setState(int state) {if (state == mState) return ;if (state == STATE_REFRESHING) { // 鏄剧ず杩涘害mArrowImageView.clearAnimation();mArrowImageView.setVisibility(View.INVISIBLE);mProgressBar.setVisibility(View.VISIBLE);} else { // 鏄剧ず绠 ご鍥剧墖mArrowImageView.setVisibility(View.VISIBLE);mProgressBar.setVisibility(View.INVISIBLE);}switch(state){case STATE_NORMAL:if (mState == STATE_READY) {mArrowImageView.startAnimation(mRotateDownAnim);}if (mState == STATE_REFRESHING) {mArrowImageView.clearAnimation();}mHintTextView.setText(R.string.xlistview_header_hint_normal);break;case STATE_READY:if (mState != STATE_READY) {mArrowImageView.clearAnimation();mArrowImageView.startAnimation(mRotateUpAnim);mHintTextView.setText(R.string.xlistview_header_hint_ready);}break;case STATE_REFRESHING:mHintTextView.setText(R.string.xlistview_header_hint_loading);break;default:}mState = state;}public void setVisiableHeight(int height) {if (height < 0)height = 0;youtParams lp = (youtParams) mContainer.getLayoutParams();lp.height = height;mContainer.setLayoutParams(lp);}public int getVisiableHeight() {return mContainer.getLayoutParams().height;}}结尾:适配器自己写,图片展示使用ImageLoader,注意ImageLoader的配置以及权限;图片实现圆形图片使用自定义ImageView;。
Android实现简单的下拉刷新控件背景:列表控件在Android App开发中⽤到的场景很多。
在以前我们⽤ListView,GradView,现在应该⼤多数开发者都已经在选择使⽤RecyclerView了,⾕歌给我们提供了这些⽅便的列表控件,我们可以很容易的使⽤它们。
但是在实际的场景中,我们可能还想要更多的能⼒,⽐如最常见的列表下拉刷新,上拉加载。
上拉刷新和下拉加载应该是列表的标配吧,基本上有列表的地⽅都要具体这个能⼒。
虽然刷新这个功能已经有各种各样的第三⽅框架可以选择,但是毕竟不是⾃⼰的嘛,今天我们就来实现⼀个⾃⼰的下拉刷新控件,多动⼿才能更好的理解。
效果图:原理分析:在coding之前,我们先分析⼀下原理,原理分析出来之后,我们才可以确定实现⽅案。
先上⼀张图,来个直观的认识:在列表上⾯有个刷新头,随着⼿指向下拉,逐渐把顶部不可见的刷新头拉到屏幕中来,⽤户能看到刷新的状态变化,达到下拉刷新的⽬的。
通过分析,我们确定⼀种实现⽅案:我们⾃定义⼀个容器,容器⾥⾯包含两个部分。
1. 顶部刷新头。
2. 列表区域。
确定好布局容器之后,我们来分析刷新头的⼏种状态把下拉刷新分为5中状态,通过不同状态间的切换实现下拉刷新能⼒。
状态间的流程图如下:整个下拉刷新的流程就如图中所⽰。
流程清楚了之后,接下来就是编写代码实现了。
代码实现:/*** @author luowang8* @date 2020-08-21 10:54* @desc 下拉刷新控件*/public class PullRefreshView extends LinearLayout {/*** 头部tag*/public static final String HEADER_TAG = "HEADER_TAG"; /*** 列表tag*/public static final String LIST_TAG = "LIST_TAG";/*** tag*/private static final String TAG = "PullRefreshView";/*** 默认初始状态*/private @Stateint mState = State.INIT;/*** 是否被拖拽*/private boolean mIsDragging = false;/*** 上下⽂*/private Context mContext;/*** RecyclerView*/private RecyclerView mRecyclerView;/*** 顶部刷新头*/private View mHeaderView;/***/private int mInitMotionY;/*** 上⼀次Y的坐标*/private int mLastMotionY;/*** ⼿指触发滑动的临界距离*/private int mSlopTouch;/*** 触发刷新的临界值*/private int mRefreshHeight = 200;/*** 滑动时长*/private int mDuring = 300;/*** ⽤户刷新监听器*/private OnRefreshListener mOnRefreshListener;/*** 刷新⽂字提⽰*/private TextView mRefreshTip;/*** 是否可拖拽, 因为在刷新头⾃由滑动和刷新状态的时候,* 我们应该保持界⾯不被破坏*/private boolean mIsCanDrag = true;/*** 头部布局*/private LayoutParams mHeaderLayoutParams;/*** 列表布局*/private LayoutParams mListLayoutParams;/*** 属性动画*/private ValueAnimator mValueAnimator;/// 分割 ////*** @param context*/public PullRefreshView(Context context) {this(context, null);}/*** @param context* @param attrs*/public PullRefreshView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0);}/*** @param attrs* @param defStyleAttr*/public PullRefreshView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);mContext = context;initView();}public RecyclerView getRecyclerView() {return mRecyclerView;}/*** 设置RecyclerView** @param recyclerView*/public void addRecyclerView(RecyclerView recyclerView) {if (recyclerView == null) {return;}View view = findViewWithTag(LIST_TAG);if (view != null) {removeView(view);}this.mRecyclerView = recyclerView;this.mRecyclerView.setTag(LIST_TAG);addView(recyclerView, mListLayoutParams);}/*** 设置⾃定义刷新头部* @param headerView*/public void addHeaderView(View headerView) {if (headerView == null) {return;}View view = findViewWithTag(HEADER_TAG);if (view != null) {removeView(view);}this.mHeaderView = headerView;this.mHeaderView.setTag(HEADER_TAG);addView(mHeaderView, mHeaderLayoutParams);}/*** @param onRefreshListener*/public void setOnRefreshListener(OnRefreshListener onRefreshListener) {mOnRefreshListener = onRefreshListener;}/*** 初始化View*/private void initView() {setOrientation(LinearLayout.VERTICAL);Context context = getContext();/** 1、添加刷新头Header */mHeaderView = LayoutInflater.from(context).inflate(yout_refresh_header, null); mHeaderView.setTag(HEADER_TAG);mRefreshTip = mHeaderView.findViewById(R.id.content);mHeaderLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,DensityUtil.dip2px(mContext, 500));this.addView(mHeaderView, mHeaderLayoutParams);/** 2、添加内容RecyclerView */mRecyclerView = new RecyclerView(context);mRecyclerView.setTag(LIST_TAG);mListLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); this.addView(mRecyclerView, mListLayoutParams);/** 3、⼀开始的时候要让Header看不见,设置向上的负paddingTop */setPadding(0, -DensityUtil.dip2px(mContext, 500), 0, 0);ViewConfiguration viewConfiguration = ViewConfiguration.get(context);mSlopTouch = viewConfiguration.getScaledTouchSlop();setState(State.INIT);}/*** 设置状态,每个状态下,做不同的事情** @param state 状态*/private void setState(@State int state) {switch (state) {case State.INIT:initState();break;case State.DRAGGING:dragState();break;case State.READY:readyState();break;case State.REFRESHING:refreshState();break;case State.FLING:flingState();break;default:break;}mState = state;}/*** 处理初始化状态⽅法*/private void initState() {// 只有在初始状态时,恢复成可拖拽mIsCanDrag = true;mIsDragging = false;mRefreshTip.setText("下拉刷新");}/*** 处理拖拽时⽅法*/private void dragState() {mIsDragging = true;}/*** 拖拽距离超过header⾼度时,如何处理private void readyState() {mRefreshTip.setText("松⼿刷新");}/*** ⽤户刷新时,如何处理*/private void refreshState() {if (mOnRefreshListener != null) {mOnRefreshListener.onRefresh();}mIsCanDrag = false;mRefreshTip.setText("正在刷新,请稍后...");}/*** ⾃由滚动时,如何处理*/private void flingState() {mIsDragging = false;mIsCanDrag = false;/** ⾃由滚动状态可以从两个状态进⼊:* 1、READY状态。
Android 上拉加载下拉刷新库2017/07/18 4009随着Android 技术的快速更新,界面也变得越来越酷炫,之前的一些应用库已经满足不了现在的需求了,但是新技术的更新也就出了很多新的库,上拉加载下拉刷新这种常用的功能也被设计师玩坏了,让我们这些程序猿也被动起来,一些比较牛的大师们顺应时代,开发出来的库那是相当的好用,也省去了我们这些小公司很多的工时,下面这一个开源库就可以满足我们大部分人的需求:先看下效果吧:SmartRefreshLayout 是最近刚开源的刷新加载库支持很多刷新动画,更是结合了Google 的SwipeRefreshLayout,现在非常流行的TwinklingRefreshLayout 、android-Ultra-Pull-To-Refresh 等,集众多开源库的优点为一体满足更多的需求:支持所有的View(AbsListView、RecyclerView、WebView....View)和多层嵌套的Layout(详细);支持自定义并且已经集成了很多炫酷的Header 和Footer (图);支持和ListView 的同步滚动和RecyclerView、AppBarLayout、CoordinatorLayout 的嵌套滚动NestedScrolling;支持在Android Studio Xml 编辑器中预览效果(图);支持分别在Default(默认)、Xml、JavaCode 三个中设置Header 和Footer;支持自动刷新、自动上拉加载(自动检测列表滚动到底部,而不用手动上拉);支持通用的刷新监听器OnRefreshListener 和更详细的滚动监听OnMultiPurposeListener;支持自定义回弹动画的插值器,实现各种炫酷的动画效果.支持设置主题来适配任何场景的App,不会出现炫酷但很尴尬的情况;支持设置多种滑动方式来适配各种效果的Header 和Footer:平移、拉伸、背后固定、顶层固定、全屏;。
Android中⽤SmartRefreshLayout实现ListView列表的数据刷新与。
这⾥⽤到的是第三⽅插件:SmartRefreshLayout效果图如下:使⽤步骤如下:1、添加远程依赖/*刷新和加载*/implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-14'implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.0-alpha-14'//没有使⽤特殊Header,可以不加这⾏2、如何在布局⽂件中使⽤,代码如下:(备注:SmartRefreshLayout分为三块:Header布局,Content布局,Footer布局。
其中,Content内容布局必须是⼀个整体。
例如,下⾯的布局包括图⽚,⽂字,列表等等,⽤⼀个ScrollView包起来。
)<?xml version="1.0" encoding="utf-8"?><LinearLayout android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="/apk/res-auto"android:orientation="vertical"xmlns:android="/apk/res/android"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:orientation="horizontal"android:background="@color/colorAccent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="标题栏"android:textSize="18dp"android:textStyle="bold"android:layout_centerInParent="true"/></RelativeLayout><yout.SmartRefreshLayoutandroid:id="@ id/Main_SRLayout"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#444444"app:srlPrimaryColor="#444444"app:srlAccentColor="@android:color/white"app:srlEnablePreviewInEditMode="true"><yout.header.ClassicsHeaderandroid:layout_width="match_parent"android:layout_height="wrap_content"/><ScrollViewandroid:id="@ id/Main_scrollView"android:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:background="@color/colorWhite"><TextViewandroid:id="@ id/Main_tvRefreshInfo"android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="gone"android:text="你好"/><ImageViewandroid:layout_width="match_parent"android:layout_height="150dp"android:scaleType="fitXY"android:src="@mipmap/corporation_detailinfo_topbg"/><com.deepreality.smartrefreshlayoutdemo.ListViewNestingandroid:id="@ id/Main_lvNewsList"android:layout_width="match_parent"android:layout_height="wrap_content"></com.deepreality.smartrefreshlayoutdemo.ListViewNesting> </LinearLayout></ScrollView><yout.footer.ClassicsFooterandroid:layout_width="match_parent"android:layout_height="wrap_content"app:srlAccentColor="@color/colorWhite"/></yout.SmartRefreshLayout></LinearLayout>3、布局⽂件知道怎么⽤了,下⾯说⼀下如何在Activity中使⽤,代码如下:其实分为以下⼏步即可:(1) 实现OnRefreshListener和OnLoadMoreListener接⼝⽅法。
AndroidRecyclerView上拉加载更多及下拉刷新功能的实现⽅法RecyclerView 已经出来很久了,但是在项⽬中之前都使⽤的是ListView,最近新的项⽬上了都⼤量的使⽤了RecycleView.尤其是瀑布流的下拉刷新,⽹上吧啦吧啦没有合适的⾃⼰总结了⼀哈。
先贴图上来看看:使⽤RecyclerView实现上拉加载更多和下拉刷新的功能我⾃⼰有两种⽅式:1.使⽤系统⾃带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现。
2.⾃定义的⾥⾯带有RecyleView的控件。
使⽤RecycleView很不好添加头部,之前在使⽤listview当中⾃⼰可以添加header和bootm,但是RecycleView好像不是那么的好操作。
对于第⼀种使⽤系统⾃带的Android.support.v4.widget.SwipeRefreshLayout来实现的,也很好⽤,但是产品⼀般不要这种下拉刷新,为了让⾃⼰显得⽜逼,他⼀般会搞⼀个⾃⼰的带有动画,这就⽐较扯淡了。
所以就只能⽤⽅法2了。
⼤致说⼀哈⽅法2的实现⽅式,⽗布局为ViewGroup,⾥⾯添加View第⼀个为控件为header第⼆个控件为RecycleView,⾄于最底部的下拉加载更多试图,通过RecycleViw的Adapter来添加。
有了思路就搞起来:package com.krain.srecyclerview.fruitview;import android.content.Context;import android.graphics.drawable.AnimationDrawable;import android.util.AttributeSet;import youtInflater;import android.view.View;import android.widget.FrameLayout;import android.widget.ImageView;import com.krain.srecyclerview.R;/*** Created by dafuShao on 2016/9/9 0009.**/public class ElizabethView extends FrameLayout {private ImageView imageView;private AnimationDrawable animationDrawable;public ElizabethView(Context context) {super(context);initview(context);}public ElizabethView(Context context, AttributeSet attrs) {super(context, attrs);initview(context);}public ElizabethView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initview(context);}private void initview(Context context){View view= LayoutInflater.from(context).inflate(yout.elizabeth_item,null);imageView=(ImageView) view.findViewById(R.id.elizabeth_im);animationDrawable= (AnimationDrawable) imageView.getBackground();addView(view);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}//开始动画public void startAnim(){animationDrawable.start();}//停⽌动画public void stopAnim(){animationDrawable.stop();}}这是头部控价很简单⾥⾯有⼀个⼩的臭蛋眼睛左右挤⼀哈的效果就不贴图了。
Android基于GridView实现自定义上拉更多和下拉刷新GridView上拉更多和下拉刷新的原理分析:1:要解决的问题:GridView和ListView不一样listView直接提供了addFootView和addHeadView两个方法,GridView是没有这两个方法的.猜想解决方案:GridView既然是不能直接添加HeadView和FootView的那么可以借助于组合控件.2:猜想产生的问题如果借助于组合控件的话那么GridView的滑动和FootView的滑动是独立的,没有相关联(也就是说,GridView的滑动与否和FootVew本身没有任何的联系)猜想解决方案:如果我们借助于,ScrollView来进行滑动的话那么在ScrollView的底部有了这个FootView的话那么是可以滑动的3:猜想产生的问题ScrollView和GridView不一样,ScrollView是不能直接给添加适配器的猜想解决方案使用ScrollView中嵌套一个布局不居中包括GridView和一个底部的FootView4:ScrollView中嵌套GridView的时候会造成显示不全的问题,需要解决ScrollView计算孩子高度的问题解决方案:可以重写GridView中的onMeasure()方法,然后给定孩子高度的最大值,然后再设计一个测量的模式.protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {intexpectHeight=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, heightMeasureSpec);}5:需要解决什么时候滑动到了底部和顶部解决方案:可以通过计算ScrollView孩子的高度,以及当前孩子滑动的顶部的相对的Y的偏移量getScrollY和当前窗体的高度getHeight.如果getScrollY+getHeight>=孩子的高度,说明滑动到底部了6:解决问题:ScrollView知道了什么时候滑动到底部,如何将上级的状态解决方案:直接通过回调的方法将状态在传递到上级页面7:整合所有的对象源代码如下:GridViewpublic class MyGridView extends GridView {public MyGridView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public MyGridView(Context context) {super(context);}public MyGridView(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int heighMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >>2, MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, heighMeasureSpec);}}MyScrollViewpublic class MyScrollView extends ScrollView{private static final String LOAD="load"; //表示的是加载数据的状态private static final String REFRESH="refresh"; //表示的是需要刷新页面的状态public MyScrollView(Context context, AttributeSet attrs) {super(context, attrs);}interface ICallBack{public void notifactionAbovePage(String tag);}ICallBack mICallBack=null;public void setOnCallBackLister(ICallBack mICallBack){this.mICallBack=mICallBack;}@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {super.onScrollChanged(l, t, oldl, oldt);if(getChildAt(0)!=null){ //有这个孩子才有意义的View childView=getChildAt(0);//第二步:通过孩子获取孩子本身的高度int childMeasuredHeight = childView.getMeasuredHeight();//第三步:获取屏幕顶部的相对y坐标int scrollY = getScrollY(); //屏幕顶部相对的y坐标//第四步:获取当前屏幕的高度int screenHeight=getHeight();if(scrollY+screenHeight==childMeasuredHeight){ //说明滑动到底部了?//这个地方是有遗留的问题的最后还需要处理if(PullUpLoadView.getDownLoadingOrNot()){return;}mICallBack.notifactionAbovePage(LOAD);}}}}PullUpLoadViewpublic class PullUpLoadView extends LinearLayout{private static final String LOAD="load"; //表示的是加载数据的状态private static final String REFRESH="refresh"; //表示的是需要刷新页面的状态MyGridView mMyGridView;LinearLayout footView;MyScrollView mMyScrollView;static boolean downLoadingOrNot=false; //判断当前是否正在加载数据public PullUpLoadView(Context context, AttributeSet attrs) {super(context, attrs);initView();}/*** 初始化数据用的*/private void initView() {//初始化布局加载器LayoutInflater mLayoutInflater= (LayoutInflater) getContext().getSystemService(YOUT_INFLATER_SERVICE);View view=mLayoutInflater.inflate(yout.scroll_grid_footview,this); //这句话的意思是把当前的布局绑定给谁findView(view);setCallBack();}/*** 写一个方法来实现回调*/private void setCallBack() {mMyScrollView.setOnCallBackLister(new OnCallBack());}/*** 定义一个接口回调到Activity页面*/public interface PullCallBack{public void load();public void refresh();}PullCallBack mPullCallBack=null;public void setPullCallBacklistener(PullCallBack mPullCallBack ){this.mPullCallBack=mPullCallBack;}/*** 上一个页面回调的状态*/private class OnCallBack implements MyScrollView.ICallBack{@Overridepublic void notifactionAbovePage(String tag) {if(tag.equals(LOAD)){ //说明需要加载数据//在这个地方我们书不是需要将这个状态发送到Activity这个页面去mPullCallBack.load();handler.sendEmptyMessage(100);downLoadingOrNot=true; //表示的额是正在加载}else{ //说明需要刷新数据}}}//刷新UIHandler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case 100: //表示的是需要显示底部的View setFootViewVisible();break;case 101: //表示的是需要隐藏底部的View setFootViewHide();downLoadingOrNot=false;break;}}};/*** 获取是否正在加载数据的这个状态* @return*/public static boolean getDownLoadingOrNot(){return downLoadingOrNot;}/*** 说明数据已经加载完成* 这个方法是Activity调用的*/public void dataDone(){handler.sendEmptyMessage(101);/*** 找下那个View* @param view*/private void findView(View view) {mMyGridView= (MyGridView) findViewById(R.id.gridView);footView= (LinearLayout) findViewById(R.id.footView);mMyScrollView= (MyScrollView) findViewById(R.id.myScrollView);}/*** 返回GridView* @return*/public MyGridView getMyGridView(){return mMyGridView;}/*** 设置footView影藏*/public void setFootViewHide(){footView.setVisibility(View.GONE);}/*** 设置FootView显示*/public void setFootViewVisible(){footView.setVisibility(View.VISIBLE);}}<?xml version="1.0"encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android" xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"><com.example.mylibrary.MyScrollViewandroid:id="@+id/myScrollView"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"<com.example.mylibrary.MyGridView android:id="@+id/gridView"android:layout_width="match_parent" android:layout_height="match_parent" android:numColumns="3"android:horizontalSpacing="10dp"android:verticalSpacing="10dp"/><LinearLayoutandroid:layout_width="match_parent" android:layout_height="50dp"android:background="#ff0077"android:gravity="center"android:id="@+id/footView"><ProgressBarandroid:layout_width="wrap_content" android:layout_height="wrap_content"style="@android:style/Widget.ProgressBar.Small" /><TextViewandroid:layout_width="wrap_content" android:layout_height="wrap_content" android:text="玩命加载中..."/></LinearLayout></LinearLayout></com.example.mylibrary.MyScrollView></RelativeLayout>。
【Android-SwipeRefreshLayout控件】下拉刷新Android⾃带API ,V4包下⾯的下拉刷新控件android.support.v4.widget.SwipeRefreshLayoutSwipeRefreshLayout只能包含⼀个控件布局例⼦:<android.support.v4.widget.SwipeRefreshLayoutandroid:id="@+id/swipeRefreshLayout"android:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:id="@+id/listView"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></android.support.v4.widget.SwipeRefreshLayout>应⽤例⼦:/*** SwipeRefreshLayout控件学习*/public class RefreshActicity extends AppCompatActivity {SwipeRefreshLayout swipeRefreshLayout;ListView listView;List<Product> list = new ArrayList<>();ProductAdapter adapter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.activity_refresh);swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);listView = findViewById(R.id.listView);//获取数据initData();//设置适配器adapter = new ProductAdapter(this, list);listView.setAdapter(adapter);//设置下拉进度的背景颜⾊,默认是⽩⾊swipeRefreshLayout.setProgressBackgroundColorSchemeResource(android.R.color.white);//设置下拉进度的肢体颜⾊swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark); //下拉时触发下拉动画,动画完毕后回调这个⽅法swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {//开始刷新,设置当前为刷新状态(刷新状态会屏蔽掉下拉事件)swipeRefreshLayout.setRefreshing(true);//耗时操作,⽐如联⽹获取数据new Handler().postDelayed(new Runnable() {@Overridepublic void run() {list.clear();initData();adapter.notifyDataSetChanged();//加载完数据,设置为不刷新状态,将下拉进度收起来swipeRefreshLayout.setRefreshing(false);}}, 2000);}});}/*** ⽣成10条List数据*/private void initData() {for (int i = 1; i <= 10; i++) {Random random = new Random();Product product = new Product("100" + i, "testdata", random.nextInt(100)); list.add(product);}}}。