OkHttp基礎(chǔ)學(xué)習(xí)(二),Post請求

簡單的Post請求,以及RecycelrView添加FooterView,上拉加載更多練習(xí),本打算是練習(xí)post請求,但寫著寫著,成了RecyclerView的練習(xí)

上拉加載更多

1. 完整的Acitivity代碼

添加FooterView,思路是Android 優(yōu)雅的為RecyclerView添加HeaderView和FooterView

public class PostActivity extends AppCompatActivity implements ResultCallback2<List<ResponseBean.ShowapiResBodyBean.NewslistBean>> {

    private Platform mPlatform;
    private int page = 1;
    private RecyclerAdapter adapter;
    private SwipeRefreshLayout swipeRefreshLayout;
    private boolean isLoading = false;
    private List<ResponseBean.ShowapiResBodyBean.NewslistBean> oldsList = new ArrayList<>();
    private HeaderAndFooterAdapter footerAdapter;
    private RecyclerView recyclerView;
    private boolean isFirst = true;
    private View footerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post);
        mPlatform = Platform.get();
        initView();
    }

    /**
     * 初始化recyclerView
     */
    private void initView() {
        //RecyclerView
        recyclerView = (RecyclerView) findViewById(R.id.activity_post_rv);
        GridLayoutManager layoutManager = new GridLayoutManager(PostActivity.this, 2);
        layoutManager.setOrientation(GridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        adapter = new RecyclerAdapter(recyclerView, R.layout.rv_item_layout);
        //使用HeaderAndFooterAdapter
        footerAdapter = new HeaderAndFooterAdapter(adapter);
        recyclerView.addItemDecoration(new RVItemDecoration(16));
        //FooterView
        footerView = this.getLayoutInflater().inflate(R.layout.footer_view_layout, recyclerView, false);
        footerView.setVisibility(View.GONE);
        footerAdapter.addFootView(footerView);
        recyclerView.setAdapter(footerAdapter);
        //SwipeRefreshLayout
        swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_post_srl);
        swipeRefreshLayout.setColorSchemeColors(Color.parseColor("#FF4081"));
        //第一次進(jìn)來有自動(dòng)刷新的效果
        swipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                swipeRefreshLayout.setRefreshing(true);
            }
        });
        swipeRefreshLayout.setEnabled(false);
        //請求第一頁
        request(page);
        //上拉加載更多
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (!recyclerView.canScrollVertically(RecyclerView.VERTICAL) && !isLoading && newState == RecyclerView.SCROLL_STATE_IDLE) {
                    footerView.setVisibility(View.VISIBLE);
                    request(++page);
                }
            }
        });
        //設(shè)置點(diǎn)擊事件
        adapter.setItemListener(new CommonBaseAdapter.onRecyclerItemClickerListener() {
            @Override
            public void onRecyclerItemClick(View view, Object data, int position) {
                ResponseBean.ShowapiResBodyBean.NewslistBean bean = (ResponseBean.ShowapiResBodyBean.NewslistBean) data;
                ToastUtils.show(PostActivity.this, bean.getTitle());
            }
        });

    }

    /**
     * 網(wǎng)絡(luò)請求
     */
    private void request(int page) {
        isLoading = true;
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();
        RequestBody requestBody = new FormBody.Builder()
                .add(Urls.KEY_APPID, Urls.APPID)
                .add(Urls.KEY_SIGN, Urls.SIGN)
                .add(Urls.KEY_NUM, Urls.NUM)
                .add(Urls.KEY_PAGE, page + "")
                .build();
        Request request = new Request.Builder().url(Urls.POST_URL).post(requestBody).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendFailResultCallback(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ResponseBody responseBody = null;
                try {
                    if (call.isCanceled()) {
                        sendFailResultCallback(new IOException("Request Canceled"));
                        return;
                    }
                    if (response.isSuccessful()) {
                        responseBody = response.body();
                        String json = responseBody.string();
                        ResponseBean responseBean = new Gson().fromJson(json, ResponseBean.class);
                        DiffUtilCallback callback = new DiffUtilCallback();
                        callback.setOldLists(oldsList);
                        oldsList.addAll(responseBean.getShowapi_res_body().getNewslist());
                        callback.setNewLists(oldsList);
                        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback);//子線程,計(jì)算差異
                        //成功回調(diào)
                        sendSuccessResultCallback(diffResult, oldsList);
                    } else {
                        sendFailResultCallback(new IOException("Request Failed"));
                    }
                } catch (Exception e) {
                    sendFailResultCallback(e);
                } finally {
                    if (null != responseBody) {
                        responseBody.close();
                    }
                }

            }
        });
    }

    @Override
    public void sendFailResultCallback(final Exception e) {
        doSomething(new Runnable() {
            @Override
            public void run() {
                //對應(yīng)在onCreate()中的創(chuàng)建方式
                //關(guān)閉刷新小圓圈
                swipeRefreshLayout.post(new Runnable() {
                        @Override
                        public void run() {
                            swipeRefreshLayout.setRefreshing(false);
                        }
                });
                footerView.setVisibility(View.GONE);
                String info = "Fail Message --> " + e.getMessage();
                ToastUtils.show(PostActivity.this, info);
                footerView.setVisibility(View.GONE);
            }
        });

    }

    @Override
    public void sendSuccessResultCallback(final DiffUtil.DiffResult diffResult, final List<ResponseBean.ShowapiResBodyBean.NewslistBean> listt) {
        isLoading = false;
        doSomething(new Runnable() {
            @Override
            public void run() {
                diffResult.dispatchUpdatesTo(footerAdapter);//將DiffUtil的結(jié)果,關(guān)聯(lián)到Adapter
                //記得將新的數(shù)據(jù),存進(jìn)adapter的List中
                adapter.setData(listt);
                footerView.setVisibility(View.GONE);
                if (isFirst) {
                    recyclerView.scrollToPosition(0);
                    isFirst = false;
                }
                if (swipeRefreshLayout.isRefreshing()) {
                    swipeRefreshLayout.setRefreshing(false);
                }
            }
        });
    }

    private void doSomething(Runnable runnable) {
        mPlatform.execute(runnable);
    }
}

代碼不多,關(guān)鍵地方有注釋

在成功請求到結(jié)果后,使用了DiffUtil代替adapter.notifyDataSetChanged()

上拉加載更多,用的recyclerView.canScrollVertically(RecyclerView.VERTICAL),返回結(jié)果代表是否可以向上垂直滑動(dòng),false就意味著到了RecycelrView的底部

當(dāng)RecycelrView到達(dá)底部時(shí),就讓原本處于View.GONEFooterView,顯示出來,當(dāng)加載完成時(shí),再View.GONE

使用HeaderAndFooterAdapter裝飾者這樣的方式,就可以不考慮各種Position問題,不需要擔(dān)心添加了FooterView后,點(diǎn)擊事件會(huì)錯(cuò)亂,RecyclerViewadapter不會(huì)受啥影響,擴(kuò)展也不會(huì)影響HeaderAndFooterAdapter,耦合度比較高


1.1 布局代碼

1.1.1 Item布局

<android.support.v7.widget.CardView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="250dp"
    android:foreground="?attr/selectableItemBackground"
    app:cardCornerRadius="4dp"
    app:cardElevation="8dp"
    app:cardPreventCornerOverlap="true"
    app:cardUseCompatPadding="true">

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

        <ImageView
            android:id="@+id/rv_item_iv"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="8.5"
            android:contentDescription="@string/app_name"
            android:maxHeight="320dp"
            android:maxWidth="180dp"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/rv_item_tv"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1.5"
            android:gravity="center"
            android:textSize="15sp" />

    </LinearLayout>
</android.support.v7.widget.CardView>

RecyclerViewitem直接使用了一個(gè)CardView,內(nèi)部是一個(gè)ImageView


1.1.2 FooterView的布局代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="45dp"
    android:gravity="center"
    android:orientation="vertical"
    android:visibility="visible">

    <android.support.v7.widget.CardView
        android:layout_width="40dp"
        android:layout_height="40dp"
        app:cardCornerRadius="20dp"
        app:cardPreventCornerOverlap="true">

        <android.support.v4.widget.ContentLoadingProgressBar
            style="?android:attr/progressBarStyleLarge"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_gravity="center" />
    </android.support.v7.widget.CardView>

</LinearLayout>

使用了CardView來顯示一個(gè)白色的圓


1.2 OkHttp Post請求

  • 創(chuàng)建okHttpClient對象
 OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();

connectTimeout(long timeout,TimeUnit unit),設(shè)置連接超時(shí)的時(shí)間

cache(Cache cache),設(shè)置緩存

cookieJar(CookieJar jar),存儲(chǔ),使用Cookie

addInterceptor(Interceptor i),添加攔截器


  • 創(chuàng)建RequestBody對象
 RequestBody requestBody = new FormBody.Builder()
                .add(Urls.KEY_APPID, Urls.APPID)
                .add(Urls.KEY_SIGN, Urls.SIGN)
                .add(Urls.KEY_NUM, Urls.NUM)
                .add(Urls.KEY_PAGE, page + "")
                .build();

RequestBody是一個(gè)abstract類,FormBodyRequestbody的子類。在建造者模式中,我個(gè)人理解FormBody屬于產(chǎn)品,而FormBody的內(nèi)部類Builder屬于具體建造者

add(String name, String value),添加查詢條件


  • Request 和 Call

Request,默認(rèn)是GET請求,通過post(RequestBody),進(jìn)行POST請求
CallGETPOST請求一樣,都是enqueue(Callback)在子線程進(jìn)行


1.3 DiffUtilCall

使用DiffUtilCall代替notifyDataSetChanged()

public class DiffUtilCallback extends DiffUtil.Callback {
    private List<ResponseBean.ShowapiResBodyBean.NewslistBean> newLists = new ArrayList<>();
    private List<ResponseBean.ShowapiResBodyBean.NewslistBean> oldLists = new ArrayList<>();

    public void setNewLists(List<ResponseBean.ShowapiResBodyBean.NewslistBean> newLists) {
        if (null != newLists) {
            this.newLists.clear();
            this.newLists.addAll(newLists);
        }
    }

    public void setOldLists(List<ResponseBean.ShowapiResBodyBean.NewslistBean> oldLists) {
        if (null != oldLists) {
            this.oldLists.clear();
            this.oldLists.addAll(oldLists);
        }
    }

    @Override
    public int getOldListSize() {
        return oldLists.size();
    }

    @Override
    public int getNewListSize() {
        return newLists.size();
    }

    /**
     *根據(jù)圖片地址,比較NewsListBean是否相同
     */
    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        ResponseBean.ShowapiResBodyBean.NewslistBean newBean = newLists.get(newItemPosition);
        ResponseBean.ShowapiResBodyBean.NewslistBean oldBean = oldLists.get(oldItemPosition);
        return oldBean.getPicUrl().equals(newBean.getPicUrl());
    }

    /**
     *只有當(dāng) areItemsTheSame 方法返回 true 時(shí),才會(huì)調(diào)用此方法
     * 比較Title是否相同
     */
    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        ResponseBean.ShowapiResBodyBean.NewslistBean newBean = newLists.get(newItemPosition);
        ResponseBean.ShowapiResBodyBean.NewslistBean oldBean = oldLists.get(oldItemPosition);
        return oldBean.getTitle().equals(newBean.getTitle());
    }

    /**
     * 得到差異的對象,最終通過 Bundle ,存進(jìn)了List<Object> payloads
     */

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        ResponseBean.ShowapiResBodyBean.NewslistBean newBean = newLists.get(newItemPosition);
        ResponseBean.ShowapiResBodyBean.NewslistBean oldBean = oldLists.get(oldItemPosition);
        Bundle diffBundle = new Bundle();
        if (!newBean.getPicUrl().equals(oldBean.getPicUrl())) {
            diffBundle.putSerializable(Strings.NEWLISTBEAN_KEY, newBean);
        }
        return diffBundle;
    }
}

主要就是重寫3個(gè)方法

使用很方便,偽碼:

 //在 OkHttp 執(zhí)行網(wǎng)絡(luò)請求的子線程中
 DiffUtilCallback callback = new DiffUtilCallback();
 callback.setOldLists(oldsList);
 callback.setNewLists(newsList);
 //在子線程中,計(jì)算差異
 DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback);

//回調(diào),在UI線程中
diffResult.dispatchUpdatesTo(footerAdapter);//將DiffUtil的結(jié)果,關(guān)聯(lián)到Adapter
//記得將新的數(shù)據(jù),存進(jìn)adapter的List中
adapter.setData(listt);

對應(yīng)的,需要重寫adapter中的onBindViewHolder(BaseViewHolder holder, int position, List<Object> payloads)


1.4 Adapter

CommonBaseAdapter是一個(gè)很簡單的適配器封裝

public class RecyclerAdapter extends CommonBaseAdapter<ResponseBean.ShowapiResBodyBean.NewslistBean> {

    public RecyclerAdapter(RecyclerView rv, @LayoutRes int itemLayoutId) {
        super(rv, itemLayoutId);
    }


    @Override
    public void bindViewData(BaseViewHolder holder, ResponseBean.ShowapiResBodyBean.NewslistBean item, int position) {
        show(holder, item);
    }


    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position, List<Object> payloads) {
        super.onBindViewHolder(holder, position, payloads);
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position);
        } else {
            Bundle bundle = (Bundle) payloads.get(0);
            ResponseBean.ShowapiResBodyBean.NewslistBean bean =
                    (ResponseBean.ShowapiResBodyBean.NewslistBean) bundle.getSerializable(Strings.NEWLISTBEAN_KEY);
            if (null != bean) {
                show(holder, bean);
            }
        }
    }

    private void show(BaseViewHolder holder, final ResponseBean.ShowapiResBodyBean.NewslistBean item) {
        final ImageView iv = holder.getView(R.id.rv_item_iv);
        final Activity mActivity = (Activity) mContext;
        iv.post(new Runnable() {
            @Override
            public void run() {
                ImgSize imgSize = getImgSize(iv);
                Glide.with(mActivity).load(item.getPicUrl()).override(imgSize.width, imgSize.height).centerCrop().into(iv);
            }
        });

        holder.setText(R.id.rv_item_tv, item.getTitle());
    }

    /**
     * 獲取 ImageView 寬高
     */
    private ImgSize getImgSize(ImageView iv) {
        ImgSize imgSize = new ImgSize();
        DisplayMetrics displayMetrics = iv.getContext().getResources()
                .getDisplayMetrics();
        ViewGroup.LayoutParams lp = iv.getLayoutParams();
        int width = iv.getWidth();
        if (width <= 0) {
            width = lp.width;
        }
        if (width <= 0) {
            width = iv.getMaxWidth();
        }
        if (width <= 0) {
            width = displayMetrics.widthPixels / 2;
        }
        int height = iv.getHeight();
        if (height <= 0) {
            height = lp.height;
        }
        if (height <= 0) {
            height = iv.getMaxHeight();
        }
        if (height <= 0) {
            height = displayMetrics.heightPixels / 2;
        }
        imgSize.width = width;
        imgSize.height = height;
        return imgSize;
    }

    private static class ImgSize {
        int width;
        int height;
    }
}

在3個(gè)參數(shù)的onBindViewHolde()方法中,先對payloads進(jìn)行判斷isEmpty()

如果payloads不為empty,就取出數(shù)據(jù)進(jìn)行加載

show()方法中,根據(jù)布局中ImageView的大小進(jìn)行加載


2. 最后

寫的跑題了,哈哈

有錯(cuò)誤請指出

共勉 :)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,581評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,922評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,096評(píng)論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,374評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,591評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,789評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,322評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,554評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容