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]

如果有什麼問題,歡迎大家指導。並相互聯繫,希望能夠通過文章互相學習。

												---財財親筆
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章