1. 更新记录:
2021/11/14:
1.更新了数据来源的 api 使用了聚合数据的 新闻 api
2.使用了 TabLayout 代替原来的 textview 组。
2021/11/13:
1.解决下拉刷新出现崩溃的现象。1.更新了数据来源的 api 2.新增设定:下拉若 5s 内没有获取到数据,就停止显示加载栏。
------------------------------------------------------------------------------------------------------
2.先上效果图:
图 1 图 2
在此先感谢我本次app开发所用的api分享者贴上文章链接 ,各位读者如有需求请移步此链接:
目前已经更换为聚合数据 api:新闻头条-新闻头条API _API数据接口_API接口调用_API接口平台-聚合数据
3. 总体思路概述:
如图1 和 图 2 app 界面简单,图 1 的最顶端是安卓原生的标题栏,图 2 的最顶端是我自己定义的标题栏,具体代码后面再说。图一标题栏下面是五个 TextView 表示五个板块,再下面是Fragment+ViewPage 的滑动页面,Fragment 里面是 Recyclerview 控件,当滑动页面滑到相应版块时上面的相应的 TextView 会变成浅蓝色。当点击 RecyclerView 里的 item 时可以进入到图二:因为所用的 api 没有返回相应的内容信息,我只好把原网址用 WebView 加载出来了,其实我也尝试过用 Jsoup 爬虫框架爬取图片和相应的信息,但效果不太好,也可能是我不太会用 Jsoup 的原因吧。
已经描述完整个 app 所有模块了,下面进行各个模块的详细说明。
4. APP所用开源库:
compile 'org.jsoup:jsoup:1.9.2'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.yalantis:phoenix:1.2.3'
compile 'com.android.support:support-v4:24.2.1'
phonix 这一条是我用的一款下拉刷新开源控件。
glide 高效加载图片用的。
cardview 卡片式布局。
okhttp 网络通信框架。
recyclerview 就不必说了。
主活动界面的 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="com.example.thinkpad.wenews.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/finance"
android:layout_weight="1"
android:layout_width="0dp"
android:text="财经"
android:gravity="center_horizontal"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/movie"
android:layout_weight="1"
android:layout_width="0dp"
android:text="电影"
android:gravity="center"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/amusement"
android:layout_weight="1"
android:layout_width="0dp"
android:text="娱乐"
android:gravity="center"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv"
android:layout_weight="1"
android:layout_width="0dp"
android:text="电视"
android:gravity="center"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/headline"
android:layout_weight="1"
android:layout_width="0dp"
android:text="头条"
android:gravity="center"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/newsLive"
android:layout_weight="1"
android:layout_width="0dp"
android:text="News"
android:gravity="center"
android:layout_height="wrap_content" />
</LinearLayout>
<com.yalantis.phoenix.PullToRefreshView
android:id="@+id/pull_to_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</com.yalantis.phoenix.PullToRefreshView>
</LinearLayout>
5. 本项目的关键点(ViewPager+fragment) 实现详解
关于 ViewPager+fragment 的模式是本项目的一个关键点:
所以如下将说明我如何利用 ViewPager+Fragment 实现本项目如上所述说的功能:
ViewPager
ViewPager 实际上和我们所熟悉的RecyclerView差不多,需要适配器,一个存储对象的List。
附上ViewPager基础知识学习链接:ViewPager 详解(一)---基本入门_android开发笔记-CSDN博客_安卓viewpager
代码:
package com.example.thinkpad.wenews;
import android.app.ProgressDialog;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.yalantis.phoenix.PullToRefreshView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ViewPager viewPager;
static List<TextView> tag;
List<Fragment> viewList;
static int tagPointer=0;
static ProgressDialog progressDialog ;
PullToRefreshView pullToRefreshView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressDialog=new ProgressDialog(MainActivity.this);
progressDialog.setMessage("正在加载内容...");
viewPager = (ViewPager) findViewById(R.id.viewpager);
pullToRefreshView=(PullToRefreshView) findViewById(R.id.pull_to_refresh);
viewList = new ArrayList<Fragment>();// 将要分页显示的View装入数组中
final amusementFragment fragment1=new amusementFragment();
final financeFragment fragment2=new financeFragment();
final armyFragment fragment3=new armyFragment();
final headlineFragment fragment4=new headlineFragment();
final tvFragment fragment5=new tvFragment();
final newsFragment fragment6=new newsFragment();
viewList.add(fragment2);
viewList.add(fragment3);
viewList.add(fragment1);
viewList.add(fragment5);
viewList.add(fragment4);
viewList.add(fragment6);
FragmentManager fragmentManager=getSupportFragmentManager();
channelPager pagerAdapter=new channelPager(fragmentManager,viewList,this);
viewPager.setAdapter(pagerAdapter);
tag=new ArrayList<>();
tag.add((TextView)findViewById(R.id.finance));
tag.add((TextView)findViewById(R.id.movie));
tag.add((TextView)findViewById(R.id.amusement));
tag.add((TextView)findViewById(R.id.tv));
tag.add((TextView)findViewById(R.id.headline));
tag.add((TextView) findViewById(R.id.newsLive));
pullToRefreshView.setOnRefreshListener(new PullToRefreshView.OnRefreshListener() {
public void onRefresh() {
switch (tagPointer){
case 0:
fragment1.GetNews();
break;
case 1:
fragment2.GetNews();
break;
case 2:
fragment3.GetNews();
break;
case 3:
fragment4.GetNews();
break;
case 4:
fragment5.GetNews();
break;
case 5:
break;
}
pullToRefreshView.setRefreshing(false);
}
});
}
}
在如上代码里:
channelPager pagerAdapter=new channelPager(fragmentManager,viewList,this);是创建了我的ViewPager适配器
在此之前我用 viewList.add(fragment2);在viewList添加几个fragment对象。
5.1 先讲一讲适配器类channelPager
package com.example.thinkpad.wenews;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by thinkpad on 2019/3/3.
*/
public class channelPager extends FragmentPagerAdapter {
List<Fragment> fragmentList=new ArrayList<>();
MainActivity mContext;
public channelPager(FragmentManager fm , List<Fragment> list,MainActivity mContext)
{
super(fm);
fragmentList=list;
this.mContext=mContext;
}
@Override
public Fragment getItem(int position) {
Log.d("getItem","good");
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList!= null ? fragmentList.size() : 0;
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
MainActivity.tagPointer=position;
switch (position) {
case 0: {
MainActivity.tag.get(0).setBackgroundColor(mContext.getResources().getColor(R.color.colorDark));
List<TextView> others = new ArrayList<TextView>();
others.addAll(MainActivity.tag);
others.remove(0);
for (TextView textView : others) {
textView.setBackgroundColor(mContext.getResources().getColor(R.color.colorWhite));
}}
break;
case 1: {
MainActivity.tag.get(1).setBackgroundColor(mContext.getResources().getColor(R.color.colorDark));
List<TextView> others = new ArrayList<TextView>();
others.addAll(MainActivity.tag);
others.remove(1);
for (TextView textView : others) {
textView.setBackgroundColor(mContext.getResources().getColor(R.color.colorWhite));
}
}
break;
case 2: {
MainActivity.tag.get(2).setBackgroundColor(mContext.getResources().getColor(R.color.colorDark));
List<TextView> others = new ArrayList<TextView>();
others.addAll(MainActivity.tag);
others.remove(2);
for (TextView textView : others) {
textView.setBackgroundColor(mContext.getResources().getColor(R.color.colorWhite));
}
}
break;
case 3: {
MainActivity.tag.get(3).setBackgroundColor(mContext.getResources().getColor(R.color.colorDark));
List<TextView> others = new ArrayList<TextView>();
others.addAll(MainActivity.tag);
others.remove(3);
for (TextView textView : others) {
textView.setBackgroundColor(mContext.getResources().getColor(R.color.colorWhite));
}
}
break;
case 4: {
MainActivity.tag.get(4).setBackgroundColor(mContext.getResources().getColor(R.color.colorDark));
List<TextView> others = new ArrayList<TextView>();
others.addAll(MainActivity.tag);
others.remove(4);
for (TextView textView : others) {
textView.setBackgroundColor(mContext.getResources().getColor(R.color.colorWhite));
}
}
break;
case 5: {
MainActivity.tag.get(5).setBackgroundColor(mContext.getResources().getColor(R.color.colorDark));
List<TextView> others = new ArrayList<TextView>();
others.addAll(MainActivity.tag);
others.remove(5);
for (TextView textView : others) {
textView.setBackgroundColor(mContext.getResources().getColor(R.color.colorWhite));
}
}
break;
}
}
}
这个类的构造方法有三个参数,FragmentManager,List<Fragment>,Mainacitivity
其中前两个是必须的因为你也可以看到super()调用父类的构造方法里传了参数fm,而List<Fragment>不消说是必须的只有MainActivity因为我需要在这个类里操作UI才特意传的。构造方法后面是两个重载函数,第一个返回固定位置的fragment对象,第二个返回数量。
setPrimaryItem是一个确定当前fragment位置的方法,在这个方法里我完成了让相应版块的TextView变成浅蓝色的功能。具体请看代码。
5.2 下面是Fragement
package com.example.thinkpad.wenews;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class amusementFragment extends Fragment {
private List<NewItem> newItems=new ArrayList<NewItem>();
private RecyclerView recyclerView_amusement;
private NewsAdapter adapter;
public amusementFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view =inflater.inflate(R.layout.layout_amusement, container, false);
recyclerView_amusement=(RecyclerView) view.findViewById(R.id.recyclerview_amusement) ;
LinearLayoutManager layoutManager=new LinearLayoutManager(getContext());
recyclerView_amusement.setLayoutManager(layoutManager);
adapter=new NewsAdapter(newItems);
recyclerView_amusement.setAdapter(adapter);
GetNews();
return view;
}
public void GetNews(){
if(!MainActivity.progressDialog.isShowing()){
MainActivity.progressDialog.show();
}
HttpUtil.sendOkhttpRequest("https://3g.163.com/touch/reconstruct/article/list/BA10TA81wangning/0-20.html", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("error11","获取错误!!!");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("成功!","12121212");
String text=response.body().string();
Log.d("response",text);
char test[]=text.toCharArray();
for(int i=0;i<9;i++)
test[i]=' ';
test[test.length-1]=' ';
Log.d("text",String.valueOf(test));
text=String.valueOf(test);
parseJSONWithJSONObject(text);
}
});
}
private void parseJSONWithJSONObject(String jsonData)
{
try{
Log.d("hello","hello");
JSONObject jsonObject=new JSONObject(jsonData);
Log.d("testtest",jsonObject.toString());
final JSONArray array=jsonObject.getJSONArray("BA10TA81wangning");
for(int i=1;i<array.length();i++)
{
NewItem one=new NewItem();
JSONObject object=array.getJSONObject(i);
one.setPictureAddress(object.getString("imgsrc"));
one.setTitle(object.getString("title"));
one.setContentAddress(object.getString("url"));
Log.d("contentadress",one.getContentAddress());
if(one.getContentAddress().toCharArray()[0]=='0')//对无用的内容地址object进筛选
{
Log.d("goodnull","truetrue!+");
continue;
}
Log.d("title12",one.getTitle());
Log.d("pic12",one.getPictureAddress());
boolean check=false;
for(NewItem c:newItems){
if(c.getTitle().equals(one.getTitle())){
check=true;
break;
}}
if(!check)
newItems.add(one);
}
Log.d("listsize","1234"+" "+newItems.size());
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if(MainActivity.progressDialog.isShowing())
MainActivity.progressDialog.dismiss();
adapter.notifyDataSetChanged();
}
});
}catch (Exception e)
{
e.printStackTrace();
}
}
}
都是一些Fragement的基本用法,我只挑了一个进行说明,其他的都差不多,感觉这样复用不够高,也许可以只写一个fragment类吧,或者先写一个接口,后面的或许更好对fragment的List进行操作,这些觉得我以后可以好好思考思考。GetNews()是我更新内容的方法。我的加载内容的模块是放在onCreatView()里的,这样其实每次划到该fragment之前就会更新数据了,但这样感觉也不是很好,各位如果有更好的方法请不吝赐教。 parseJSONWithJSONObject()是解析Json对象的,关于Json的解析我代码在上面建议配合json源码看看。
httpsendrequest就不说了,是一个发起请求的方法如下:
public class HttpUtil {
private String channel="";
private final static String apikey="LzQUsyWYuvT5kNqAAuUuY1pBmhhS37V7";
public static void sendOkhttpRequest(String address,okhttp3.Callback callback){
//建立RequestBody对象存放待提交的参数,参数有 apikey,text,userid.
/*RequestBody requestBody=new FormBody.Builder()
.add("key",apikey)
.add("info",text)
.add("userid","128")
.build();*/
OkHttpClient client =new OkHttpClient();
Request request=new Request.Builder()
.url(address)
.build();
client.newCall(request).enqueue(callback);//enqueue方法在内部开好了子线程并最终将结果回调到okhttp3.Callback当中。
}
}
5.3 RecyclerView的item类代码:
public class NewItem {
private String title;
private String pictureAddress;
private String contentAddress;
public String getContentAddress() {
return contentAddress;
}
public void setContentAddress(String contentAddress) {
this.contentAddress = contentAddress;
}
public String getPictureAddress() {
return pictureAddress;
}
public void setPictureAddress(String pictureAddress) {
this.pictureAddress = pictureAddress;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
6 总结以及源码下载链接
基本就到此结束了。下载源码的同时,希望能点个赞或者GitHub点个星,感激不尽!
github的项目地址:https://github.com/DhyanaCoder/WeNews/tree/master 欢迎浏览以及下载。