美文网首页
OkHttp基础学习(二),Post请求

OkHttp基础学习(二),Post请求

作者: 英勇青铜5 | 来源:发表于2017-01-09 14:04 被阅读274次

    简单的Post请求,以及RecycelrView添加FooterView,上拉加载更多练习,本打算是练习post请求,但写着写着,成了RecyclerView的练习

    上拉加载更多

    1. 完整的Acitivity代码

    添加FooterView,思路是Android 优雅的为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"));
            //第一次进来有自动刷新的效果
            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);
                    }
                }
            });
            //设置点击事件
            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());
                }
            });
    
        }
    
        /**
         * 网络请求
         */
        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);//子线程,计算差异
                            //成功回调
                            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() {
                    //对应在onCreate()中的创建方式
                    //关闭刷新小圆圈
                    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的结果,关联到Adapter
                    //记得将新的数据,存进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);
        }
    }
    

    代码不多,关键地方有注释

    在成功请求到结果后,使用了DiffUtil代替adapter.notifyDataSetChanged()

    上拉加载更多,用的recyclerView.canScrollVertically(RecyclerView.VERTICAL),返回结果代表是否可以向上垂直滑动,false就意味着到了RecycelrView的底部

    RecycelrView到达底部时,就让原本处于View.GONEFooterView,显示出来,当加载完成时,再View.GONE

    使用HeaderAndFooterAdapter装饰者这样的方式,就可以不考虑各种Position问题,不需要担心添加了FooterView后,点击事件会错乱,RecyclerViewadapter不会受啥影响,扩展也不会影响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直接使用了一个CardView,内部是一个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来显示一个白色的圆


    1.2 OkHttp Post请求

    • 创建okHttpClient对象
     OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .build();
    

    connectTimeout(long timeout,TimeUnit unit),设置连接超时的时间

    cache(Cache cache),设置缓存

    cookieJar(CookieJar jar),存储,使用Cookie

    addInterceptor(Interceptor i),添加拦截器


    • 创建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是一个abstract类,FormBodyRequestbody的子类。在建造者模式中,我个人理解FormBody属于产品,而FormBody的内部类Builder属于具体建造者

    add(String name, String value),添加查询条件


    • Request 和 Call

    Request,默认是GET请求,通过post(RequestBody),进行POST请求
    CallGETPOST请求一样,都是enqueue(Callback)在子线程进行


    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();
        }
    
        /**
         *根据图片地址,比较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());
        }
    
        /**
         *只有当 areItemsTheSame 方法返回 true 时,才会调用此方法
         * 比较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 ,存进了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个方法

    使用很方便,伪码:

     //在 OkHttp 执行网络请求的子线程中
     DiffUtilCallback callback = new DiffUtilCallback();
     callback.setOldLists(oldsList);
     callback.setNewLists(newsList);
     //在子线程中,计算差异
     DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callback);
    
    //回调,在UI线程中
    diffResult.dispatchUpdatesTo(footerAdapter);//将DiffUtil的结果,关联到Adapter
    //记得将新的数据,存进adapter的List中
    adapter.setData(listt);
    

    对应的,需要重写adapter中的onBindViewHolder(BaseViewHolder holder, int position, List<Object> payloads)


    1.4 Adapter

    CommonBaseAdapter是一个很简单的适配器封装

    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个参数的onBindViewHolde()方法中,先对payloads进行判断isEmpty()

    如果payloads不为empty,就取出数据进行加载

    show()方法中,根据布局中ImageView的大小进行加载


    2. 最后

    写的跑题了,哈哈

    有错误请指出

    共勉 :)

    相关文章

      网友评论

          本文标题:OkHttp基础学习(二),Post请求

          本文链接:https://www.haomeiwen.com/subject/unqhbttx.html