简单的Android端新闻App的实现。

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接口调用_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 欢迎浏览以及下载。

原文链接:https://blog.csdn.net/qq_41105058/article/details/88130013?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165277607816781432928582%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165277607816781432928582&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~times_rank-15-88130013-null-null.nonecase&utm_term=%E6%96%B0%E9%97%BB

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
文明发言,共建和谐米科社区
提交
头像

昵称

取消
昵称表情图片

    暂无评论内容