先来段引言:okhttp真的好用,java基础真的要学好。哈哈哈~
看看效果吧:
进入正题,整体思路是:用TabLayout和ViewPager划分出四个新闻类型的区域,这四个区域分别用四个fragment来装,fragment里面就是一个RecyclerView,用okhttp获取数据并放到RecyclerView中。
java和xml文件概览:
首先建立主布局:NewsListActivity
<?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=".view.activity.NewsListActivity"> <android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="60dp" android:background="#FF6666" app:tabTextColor="#FFC0C0" app:tabSelectedTextColor="#fff" app:tabMode="fixed" app:tabIndicatorColor="#fff"/> <!--app:tabIndicatorColor="#CC33FF" app:tabIndicatorHeight="8dp" 表示提示条高度及颜色--> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="wrap_content" android:layout_height="0dp" android:layout_weight="1"> </android.support.v4.view.ViewPager> </LinearLayout>
很简单,就是LinearLayout里面放一个TabLayout和一个ViewPager。
然后通过fragmentPagerAdapter来加载四个fragment:
此处不再赘述,用法详见:Android 导航条效果实现(六) TabLayout+ViewPager+Fragment
我这里没有用到动态加载,后续会更新。
下一步就是每个fragment里面都放一个RecyclerView,这里放出社会类新闻的fragment,以此类推...
然后就是往RecyclerView里面填数据,新建一个子项布局news_item.xml,里面用LinearLayout装ImageView+textView存放新闻的图片和标题时间等,也是比较简单的,代码就不放了:
怎么放数据进去,首先要获取数据,那么就要用到好用的okhttp了。
下面这段代码就是okhttp的异步加载:
public void asyncHttpRequest(String type){ Log.i(TAG, "加载到asyncHttpRequest()"); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://v.juhe.cn/toutiao/index?type="+type+"&key=65d4c89f2460e131bd8b288f3f70bff6") .build(); okhttp3.Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { ToastUtil.showToast(context,"新闻加载失败"); } @Override public void onResponse(Call call, final Response response) throws IOException { Log.d(TAG, "请求成功"); Log.d(">>>onResponse>>>当前线程", Thread.currentThread().getName()); final String responseData = response.body().string(); Log.d(TAG, responseData); parseData(responseData); activity.runOnUiThread(new Runnable() { @Override public void run() { ToastUtil.showToast(activity,"成功获取数据"); } }); } }); }
OkHttp用法详见:OKHttp使用详解(菜鸟一枚,暂不懂原理,只能当搬运工啦,嘿嘿~)
这里通过传入申请到的Api来获取数据,返回的response就是我获取到的数据啦,这里要注意,要打印出来要用到response.body().string(),而这个方法只能调用一次,所以声明一个变量responseData来存放。这是json格式的字符串,我们要把它解析出来为我们所用。首先我们要了解其结构,直接把这个带AppKey和参数的网址打开是这样的字符串:
那么我们就要建立一个对应的实体类,这里有两个方法:一个是通过在线Json解析工具来生成实体类然后复制到我们新建的实体类中,还有一种是在Android studio中安装GsonFormat插件,自动生成。都挺好用,看个人习惯。Gsonformat就像下面这样,Alt+S打开插件,把json数据粘贴上去,如果格式正确,点击ok就自动生成代码啦。
现在我们就有能装我们解析出来的数据的实体类啦,然后就开始解析:
通过谷歌官方的解析库Gson来解析成我们要的格式,这里解析成了一个List,随手打印一下,可以拿到标题啦,再写几句代码测试下放到界面上(注意这里要在主线程即UI线程进行),发现可以啦,于是可以愉快地进行展示操作了。
现在我们已经获取到我们想要的全部数据了,接下来就是放进界面的过程,即放入RecyclerView。这里我们需要一个自定义NewsAdapter来将RecyclerView和数据关联起来:
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> { private List<NewsBean.Result.Data> mNewsList; public NewsAdapter(List<NewsBean.Result.Data> mNewsList) { this.mNewsList = mNewsList; } class ViewHolder extends RecyclerView.ViewHolder { private LinearLayout linearLayout; private ImageView newsImage; private TextView newsTitle; private TextView newsSrc; private TextView newsTime; public ViewHolder(View view) { super(view); linearLayout = view.findViewById(R.id.linear_layout); newsImage = view.findViewById(R.id.iv_news_list_image); newsTitle = view.findViewById(R.id.tv_news_list_title); newsSrc = view.findViewById(R.id.tv_news_list_real_type); newsTime = view.findViewById(R.id.tv_news_list_time); } } @Override public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) { final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { final NewsBean.Result.Data news = mNewsList.get(position); String image = news.getThumbnail_pic_s().toString(); // Log.i(">>>adapter>>>>>image", mNewsList.get(0).getThumbnail_pic_s()); Glide.with(holder.itemView.getContext()).load(image).into(holder.newsImage); holder.newsTitle.setText(news.getTitle()); holder.newsSrc.setText(news.getAuthor_name()); holder.newsTime.setText(news.getDate()); holder.linearLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(v.getContext(),NewsInfoActivity.class); intent.putExtra("url",news.getUrl()); v.getContext().startActivity(intent); } }); } @Override public int getItemCount() { return mNewsList.size(); } }
关于这个RecyclerView的用法我前面的文章有demo,也不详细讲了。这样子我们的数据展示工作就完成啦。最后想点击新闻列表中的每个单项查看新闻的详细信息,就新建了一个NewsInfoActivity,里面就一个控件WebView,用来加载网页。在NewsAdapter里面将我们数据中的网页url取出来,传到WebView进行加载就行了。
在Adapter中传值:
在NewsInfoActivity中接收并加载:
大功告成!
.
.
.
.
.
.
最后是总结:
1.关于okhttp的同步和异步加载,对于同步请求在请求时需要开启子线程,请求成功后需要跳转到UI线程修改UI。异步加载不用再次开启子线程,但回调方法是执行在子线程中,所以在更新UI时还要跳转到UI线程中。
2.response.body().string这个方法只能调用一次,超过一次会报错,想重复使用就要放到一个变量中。
3.本来只想把网络请求这部分内容封装,结果发现返回的值在try里面怎么都取不出来,困扰了一个星期,java基础不过关。。。无奈,解析和UI操作都一起封装了,不过这样倒是让主界面代码看起来简洁了许多。
4.试着用retrofit写了个demo,以为代码量会比okhttp少很多,想进行替换,结果发现差不多,应该是我还没理解到其精髓,后续应该会将此程序改为retrofit。
5.过程中遇到好多异常,忘了记录下来解决方法,以后注意。