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)系作者
  • 序言:七十年代末括改,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子家坎,更是在濱河造成了極大的恐慌嘱能,老刑警劉巖吝梅,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惹骂,居然都是意外死亡苏携,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門对粪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來右冻,“玉大人,你說我怎么就攤上這事衩侥」酰” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵茫死,是天一觀的道長跪但。 經(jīng)常有香客問我,道長峦萎,這世上最難降的妖魔是什么屡久? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮爱榔,結(jié)果婚禮上被环,老公的妹妹穿的比我還像新娘。我一直安慰自己详幽,他們只是感情好筛欢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唇聘,像睡著了一般版姑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迟郎,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天剥险,我揣著相機(jī)與錄音,去河邊找鬼宪肖。 笑死表制,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的控乾。 我是一名探鬼主播么介,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阱持!你這毒婦竟也來了夭拌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸽扁,沒想到半個(gè)月后蒜绽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桶现,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年躲雅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骡和。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡相赁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慰于,到底是詐尸還是另有隱情钮科,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布婆赠,位于F島的核電站绵脯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏休里。R本人自食惡果不足惜蛆挫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妙黍。 院中可真熱鬧悴侵,春花似錦、人聲如沸拭嫁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽做粤。三九已至巴元,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驮宴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工呕缭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堵泽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓恢总,卻偏偏與公主長得像迎罗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子片仿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

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