Android实现下拉刷新和上拉加载数据(网络请求数据)

简介

在安卓开发中,我们会经常遇到上拉加载和下拉刷新的功能,通过网络请求拿到数据然后添加到控件上,之前自己也在网上搜索过一些文章,但基本上用处不大,要么是效果不一样,要么是贴的代码不完整,从而导致功能无法正常使用(很蛋疼),所以在摸索了一阵之后,完成了一个大致OK的demo,供大家参考。有问题还望大家指正出来,感激不尽。。

老规矩,先贴效果图

demo效果图

使用到得控件和工具简介:

  • RefreshLayout(上拉加载下拉刷新控件)
  • Adapter(加载数据的适配器)
  • RecyclerView(加载数据的view控件)
  • Handler(网络通信)

好了,开始上代码吧!!!

既然用到了网络,就要放入网络权限

<uses-permission android:name="android.permission.INTERNET"/>

布局XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/cyan">

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dimen_60_dip"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:gravity="center"
            android:text="@string/jokes"
            android:textColor="@color/black"
            android:textSize="@dimen/dimen_22_dip" />

        <ImageView
            android:id="@+id/joke_img_back"
            android:layout_width="@dimen/dimen_30_dip"
            android:layout_height="@dimen/dimen_30_dip"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            android:layout_marginStart="@dimen/dimen_10_dip"
            app:srcCompat="@mipmap/back" />

    </RelativeLayout>

    <com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:id="@+id/activity_joke_refreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:srlEnablePreviewInEditMode="false">

        <com.scwang.smartrefresh.layout.header.ClassicsHeader
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:srlAccentColor="@color/black"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/joke_img_load"
                android:src="@mipmap/load"
                android:layout_marginTop="@dimen/dimen_20_dip"
                android:layout_gravity="center"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dimen_70_dip" />

            <android.support.v7.widget.RecyclerView
                android:id="@+id/joke_rv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="@dimen/dimen_80_dip"
                android:layout_marginTop="@dimen/dimen_8_dip"
                android:src="@mipmap/bottom_bg"/>

            <View
                android:layout_width="match_parent"
                android:layout_height="@dimen/dimen_0.5_dip"
                android:layout_marginTop="@dimen/dimen_5_dip"
                android:background="@color/darkgrey"/>

        </LinearLayout>

        <com.scwang.smartrefresh.layout.footer.ClassicsFooter
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:srlAccentColor="@color/black"/>

    </com.scwang.smartrefresh.layout.SmartRefreshLayout>

</LinearLayout>

注:这个SmartRefreshLayout是自定义的一个框架,源自GitHub,gradle里面添加一下就可以啦

MainActivity.class

/**
 * activity
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    /** activity */
    Activity activity = this;

    /** 请求的key */
    public static String Joke_APPKEY = "e9bbc8a5de090451bd5da96dc574a94a";
    /** 请求随机获取笑话的URL地址 */
    public static final String HTTPURLS = "http://v.juhe.cn/joke/randJoke.php?";

    /** 执行动画对象 */
    private static Animation rotateAnimation;

    /** 网络请求返回码 */
    static final int SUCC_CODE = 0;

    /** 返回按钮和加载中按钮 */
    ImageView joke_img_back, joke_img_load;

    /** 加载内容的RV */
    RecyclerView joke_rv;

    /** 添加数据的适配器 */
    JokeLVAdapter adapter;

    /** 自定义刷新和加载的标识,默认为false */
    boolean isRef, isLoad = false;

    /** swf:这个是上拉刷新和加载框架 */
    RefreshLayout activity_joke_refreshLayout;

    /** 使用handler请求网络数据并在handleMessage里面处理返回操作 */
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //如果是刷新和加载的请求标识,直接刷新adapter加载数据
            if(msg.arg1 == SUCC_CODE && isLoad || isRef){
                adapter.notifyDataSetChanged();
            }
            //否则的话就相当于首次进入加载,先关闭动画,然后把数据加载到RV上
            else if(msg.arg1 == SUCC_CODE){
                joke_img_load.clearAnimation();
                joke_img_load.setVisibility(View.GONE);
                adapter = new JokeLVAdapter(activity, datas);
                joke_rv.setLayoutManager(new LinearLayoutManager(activity));
                joke_rv.setAdapter(adapter);
                //当rv的item点击之后进入此方法,并在openWindow处理逻辑
                adapter.setLinster(new JokeLVAdapter.ItemOnClickLinster() {
                    @Override
                    public void textItemOnClick(View view, int position) {
                        Log.i("activity", "----->position=" + position);
                        //打开一个窗口
                        openWindow(position);
                    }
                });
            }else{
                //数据加载失败,关闭动画,并提示
                joke_img_load.clearAnimation();
                joke_img_load.setVisibility(View.GONE);
                Toast.makeText(activity, R.string.getDataError, Toast.LENGTH_SHORT).show();
            }
        }
    };

    /**
     * 通过position去查找唯一的一条信息
     * @param position
     */
    private void openWindow(int position) {
        Toast.makeText(activity, "当前点击item的下标为" + position, Toast.LENGTH_SHORT).show();
    }

    /** 设置一个集合,用来存储网络请求到的数据 */
    List<JokesNew> datas = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去掉标题栏
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        initView();
        initData();
        //修改状态栏颜色
        StatusBarCompat.setStatusBarColor(activity, ContextCompat.getColor(activity, R.color.cyan));
    }

    public void initView() {
        //获取控件id
        joke_img_back = findViewById(R.id.joke_img_back);
        joke_img_load = findViewById(R.id.joke_img_load);
        joke_rv = findViewById(R.id.joke_rv);
        activity_joke_refreshLayout = findViewById(R.id.activity_joke_refreshLayout);

        //设置refreshLayout的一些操作
        //越界回弹
        activity_joke_refreshLayout.setEnableOverScrollBounce(false);

        //在刷新或者加载的时候不允许操作视图
        activity_joke_refreshLayout.setDisableContentWhenRefresh(true);
        activity_joke_refreshLayout.setDisableContentWhenLoading(true);

        //监听列表在滚动到底部时触发加载事件(默认true)
        activity_joke_refreshLayout.setEnableAutoLoadmore(false);


        /**
         * 正在下拉刷新数据中
         */
        activity_joke_refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
                Log.i("activity", "下拉刷新");
                //数据加载完后调用这行结束刷新
                isRef = true;
                handler.post(getRefreshDatas);
            }
        });

        /**
         * 正在上拉加载数据中
         */
        activity_joke_refreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
            @Override
            public void onLoadmore(RefreshLayout refreshlayout) {
                Log.i("activity", "上拉加载");
                isLoad = true;
                handler.post(getLoadmoreDatas);
            }
        });

        //退出
        joke_img_back.setOnClickListener(this);
    }

    public void initData() {
        //将xml的控件设置为可见状态,并开启一个动画去过渡加载数据中的空白页面
        joke_img_load.setVisibility(View.VISIBLE);
        openA(activity, joke_img_load);
        //请求
        handler.post(getDatas);
    }

    /**
     * getDatas
     */
    Runnable getDatas = new Runnable() {
        @Override
        public void run() {
            HttpRequest.get(HTTPURLS + "&key=" + Joke_APPKEY, new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.i("activity", "数据获取成功");
                    String result = response.body().string();
                    JsonDta(result);
                }
            });
        }
    };

    /**
     * 解析json
     * @param result
     */
    private void JsonDta(String result) {
        Message message = handler.obtainMessage();
        //解析json数据并赋值给SJJokeNow对象
        SJJokeNow obj = new Gson().fromJson(result, SJJokeNow.class);
        //不成功时,通知handler数据加载失败
        if(obj.getError_code() != 0){
            message.arg1 = obj.getError_code();
            handler.sendMessage(message);
        }else {
            //成功时,判断位
            if(isRef){
                Log.i("activity", "------>" + obj.getReason());
                List<JokesNew> json = new ArrayList<>();
                for (int i = 0; i < obj.getResult().size(); i ++){
                    JokesNew info = new JokesNew();
                    info.setHashId(obj.getResult().get(i).getHashId());
                    info.setContent(obj.getResult().get(i).getContent());
                    info.setUnixtime(obj.getResult().get(i).getUnixtime());
                    json.add(info);
                }
                for(int i = 0 ; i < datas.size() ; i++) {
                    json.add(datas.get(i));
                }
                datas.clear();
                for(int i = 0 ; i < json.size() ; i++) {
                    datas.add(json.get(i));
                }
                isRef = false;
            }else {
                Log.i("activity", "------>" + obj.getReason());
                for (int i = 0; i < obj.getResult().size(); i ++){
                    JokesNew info = new JokesNew();
                    info.setHashId(obj.getResult().get(i).getHashId());
                    info.setContent(obj.getResult().get(i).getContent());
                    info.setUnixtime(obj.getResult().get(i).getUnixtime());
                    datas.add(info);
                }
            }
            message.arg1 = obj.getError_code();
            handler.sendMessage(message);
        }
    }

    /**
     * 加载刷新的数据
     */
    Runnable getRefreshDatas = new Runnable() {
        @Override
        public void run() {
            HttpRequest.get(HTTPURLS + "&key=" + Joke_APPKEY, new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    activity_joke_refreshLayout.finishRefresh(0000 , false);
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.i("activity", "数据获取成功");
                    activity_joke_refreshLayout.finishRefresh(0000 , true);
                    String result = response.body().string();
                    JsonDta(result);
                }
            });
        }
    };

    /**
     * 加载上拉的数据
     */
    Runnable getLoadmoreDatas = new Runnable() {
        @Override
        public void run() {
            HttpRequest.get(HTTPURLS + "&key=" + Joke_APPKEY, new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                    activity_joke_refreshLayout.finishLoadmore(0000 , false);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.i("activity",  "数据获取成功");
                    activity_joke_refreshLayout.finishLoadmore(0000 , true);
                    String result = response.body().string();
                    JsonDta(result);
                }
            });
        }
    };

    @Override
    public void onClick(View v) {
        int temdId = v.getId();
        if(temdId == R.id.joke_img_back){
            finish();
        }
    }

    /**
     * 开启一个动画
     * @param img
     */
    public static void openA(Activity activity, ImageView img){
        //加载loading动画
        rotateAnimation = AnimationUtils.loadAnimation(activity, R.anim.loading);
        LinearInterpolator interpolator = new LinearInterpolator();
        rotateAnimation.setInterpolator(interpolator);
        img.startAnimation(rotateAnimation);
    }

}

注:本文的注释写的已经相当清楚了,所以就不多做解释啦。

JokeLVAdapter.class

/**
 * 添加数据的适配器
 */
public class JokeLVAdapter extends RecyclerView.Adapter<JokeLVAdapter.ViewHolder> {

    /** 上下文 */
    Activity context;

    /** 数据源 */
    List<JokesNew> data;

    /** 控件 */
    LayoutInflater inflater;

    /**
     * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
     */
    LruCache<String, BitmapDrawable> mMemoryCache;

    /**
     * 这里的data作为数据源从activity传入
     * @param activity
     * @param datas
     */
    public JokeLVAdapter(Activity activity, List<JokesNew> datas){
        this.context = activity;
        this.data = datas;

        //获取布局
        inflater = LayoutInflater.from(activity);


        // 获取应用程序最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, BitmapDrawable>(cacheSize) {
            @Override
            protected int sizeOf(String key, BitmapDrawable drawable) {
                return drawable.getBitmap().getByteCount();
            }
        };
    }


    /**
     * 加载布局,相当于activity的onCreate方法
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.joke_lv_item, parent, false);
        return new ViewHolder(view);
    }

    /**
     * 绑定数据
     * @param viewHolder
     * @param position
     */
    @Override
    public void onBindViewHolder(final ViewHolder viewHolder, int position) {
        viewHolder.joke_lv_txtconent.setText(data.get(position).getContent());
        viewHolder.joke_lv_txttime.setText("时间戳:" + data.get(position).getUnixtime() + "");

        //设置tag
        viewHolder.itemView.setTag(position);
    }

    /**
     * 数据源的内容大小
     * @return
     */
    @Override
    public int getItemCount() {
        return data.size();
    }

    /**
     * //自定义的ViewHolder,持有每个Item的的所有界面元素
     */
    public class ViewHolder extends RecyclerView.ViewHolder {

        /** 获取item的控件 */
        public TextView joke_lv_txttime;
        public TextView joke_lv_txtconent;
        public LinearLayout lin_alljoke;

        public ViewHolder(View rootView) {
            super(rootView);
            this.joke_lv_txtconent = rootView.findViewById(R.id.joke_lv_txtconent);
            this.joke_lv_txttime = rootView.findViewById(R.id.joke_lv_txttime);
            this.lin_alljoke = rootView.findViewById(R.id.lin_alljoke);

            //设置item的点击事件
            this.lin_alljoke.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Linster.textItemOnClick(v, getPosition());
                }
            });
        }
    }

    public ItemOnClickLinster Linster;

    public void setLinster(ItemOnClickLinster linster) {
        Linster = linster;
    }

    public interface ItemOnClickLinster{
        void textItemOnClick(View view, int position);
    }


}

注:同上,注释简单明了,不多废话。继续,下一步:

貌似没有下一步啦,其实吧,整个功能没有难懂的地方,只要理解了代码,换到自己的项目里面能够熟练的使用就可以,可以根据不同的需求去定制不同的实现方式,而这里的这种方式只是一种,给大家借鉴一下而已,同时有什么好方法也可以给我推荐下,代码的不足也可以指出,静等各位大佬佳音!!

附上demo源码,因不太熟练GitHub,所以这里的链接还是csdn的。

点这里下载源码,快,戳我戳我…

本文引用GitHub的刷新框架,不拥有解释权,如果想进一步了解刷新框架,请前往下面的地址阅读
https://github.com/scwang90/SmartRefreshLayout

q:486789970
email:[email protected]

如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。

												---财财亲笔

Guess you like

Origin blog.csdn.net/qq_35840038/article/details/85061597