又到周末了,最近一周有点时间搞点东西来耍。由于工作的性质,导致我接触的互联网应用几乎没有,所以本身写的代码可能跟互联网应用有很大的差别,我会不断的更新优化应用。
本项目最开始只是为了满足我自己搞一个Retrofit+Rxjava+Gson 的小例子,最终演变成了一个可以玩的小项目,数据来源:易源数据。后面打算加更多的内容,比如视频,图片之类的,或者登陆,设置等等,也会不断的去刚改项目的架构以及优化部分代码,后面尽量写成MVP模式的应用。
目前这个demo,主要用到以下几个库:
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
//Retrofit网络请求框架
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.google.code.gson:gson:2.8.5'
//下拉加载和下拉刷新
implementation 'com.github.userswlwork:pull-to-refresh:1.0.0'
// butterknift 框架
implementation 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
//Glide
implementation 'com.github.bumptech.glide:glide:4.7.1'
//网页加载动画
implementation 'com.github.ybq:Android-SpinKit:1.1.0'
// EventBus
implementation 'org.greenrobot:eventbus:3.0.0'
主要实现的功能有 新闻资源的获取解析(Retrofit+Rxjava+Gson),下拉刷新(pull-to-refresh),注解释框架ButterKnife,用Glide加载新闻列表图片,在等待网页加载时的动画效果(Android-spinkit),以及数据分发暂时用EventBus来处理,链接打开的网页,使用了腾讯的TBS x5 内核,既然是新闻类型的应用肯定是有推送的,我在这里用了极光推送,初次使用还不是太熟这个用法,只是按照自己的理解 做了一点处理,详情见demo。
项目目前主打 新闻浏览功能,所以目前的架构还没搞, 目前整个功能项是由单个Activit+fragment+viewpage 的方式实现
package com.fly.kuaixun.ui;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import com.fly.kuaixun.R;
import com.fly.kuaixun.adapter.NewsViewPagerAdapter;
import com.fly.kuaixun.tool.LogUtil;
import com.fly.kuaixun.tool.ToolsUtil;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class NewsActivity extends AppCompatActivity{
private String[] titles = new String[]{
"头条", "社会", "国内", "国际", "娱乐",
"体育", "军事", "科技", "财经", "时尚"};
@BindView(R.id.tabs)
TabLayout mTabs;
@BindView(R.id.vp_content)
ViewPager mVpContent;
private NewsViewPagerAdapter mFragmentAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setFlags(flag, flag);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
loadMovieChannel();
EventBus.getDefault().register(this);
}
/**
* 加载电影频道
*/
private void loadMovieChannel() {
List<NewsFragment> mNewsFragmentList = new ArrayList<>();
for (int i = 0; i < titles.length; i++) {
mNewsFragmentList.add(createListFragments(i));
}
if (mFragmentAdapter == null) {
mFragmentAdapter = new NewsViewPagerAdapter(getSupportFragmentManager(), mNewsFragmentList, Arrays.asList(titles));
} else {
//刷新fragment
mFragmentAdapter.setFragments(getSupportFragmentManager(), mNewsFragmentList, Arrays.asList(titles));
}
mVpContent.setAdapter(mFragmentAdapter);
mTabs.setupWithViewPager(mVpContent);
ToolsUtil.dynamicSetTabLayoutMode(mTabs);
mTabs.setTabTextColors(getResources().getColor(R.color.white), getResources().getColor(R.color.black));
}
private NewsFragment createListFragments(int index) {
NewsFragment fragment = new NewsFragment();
Bundle bundle = new Bundle();
bundle.putString("type", titles[index]);
fragment.setArguments(bundle);
return fragment;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent1(String text) {
switch (text){
case "Jpush_message_come":
LogUtil.e("================= 收到极光推送消息");
showNormalDialog();
break;
}
}
/**
* 一个dialog,用来提醒极光推送效果
*/
private void showNormalDialog() {
final AlertDialog.Builder
normalDialog =
new AlertDialog.Builder(NewsActivity.this);
normalDialog.setTitle("您有新的消息!");
normalDialog.setMessage("您有新的消息,请及时查阅!");
normalDialog.show();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
package com.fly.kuaixun.ui;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.handmark.pulltorefresh.library.ILoadingLayout;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshListView;
import com.fly.kuaixun.R;
import com.fly.kuaixun.bean.NewBean;
import com.fly.kuaixun.bean.NewContentBean;
import com.fly.kuaixun.adapter.NewsListAdapter;
import com.fly.kuaixun.mode.NewsService;
import com.fly.kuaixun.tool.AndroidScheduler;
import com.fly.kuaixun.tool.UrlTool;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import butterknife.BindView;
import butterknife.ButterKnife;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Subscriber;
import rx.schedulers.Schedulers;
public class NewsFragment extends Fragment implements AdapterView.OnItemClickListener {
@BindView(R.id.listView)
PullToRefreshListView listView;
protected Handler mHandler = new Handler();
private List<NewContentBean> news = new ArrayList<>();
private NewsListAdapter adapter;
private String mMovieGenres;
private int newsPage = 1;
private int newsPageSize;
private Context mContext;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_news, container, false);
ButterKnife.bind(this, view);
initUi(view);
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
private void initUi(View view) {
if (getArguments() != null) {
mMovieGenres = getArguments().getString("type");
}
initPullToRefreshListView();
listView.setOnItemClickListener(this);
loadNews(mMovieGenres, newsPage);
adapter = new NewsListAdapter(mContext, news);
listView.setAdapter(adapter);
}
private void initPullToRefreshListView() {
listView.setMode(PullToRefreshBase.Mode.BOTH);
ILoadingLayout startLabels = listView.getLoadingLayoutProxy(true, false);
startLabels.setPullLabel("下拉刷新");
startLabels.setRefreshingLabel("正在刷新...");
startLabels.setReleaseLabel("释放立即刷新");
ILoadingLayout endLabels = listView.getLoadingLayoutProxy(false, true);
endLabels.setPullLabel("上拉刷新");
endLabels.setRefreshingLabel("正在载入...");
endLabels.setReleaseLabel("释放立即刷新");
listView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2<ListView>() {
@Override
public void onPullDownToRefresh(PullToRefreshBase<ListView> pullToRefreshBase) {
// newsType = titles[(int) (Math.random() * (titles.length))]; //随机去掉
newsPage = 1;
loadNews(mMovieGenres, newsPage);
}
@Override
public void onPullUpToRefresh(PullToRefreshBase<ListView> pullToRefreshBase) {
if (newsPage == newsPageSize) {
Toast.makeText(getActivity(), "我是有底线的!", Toast.LENGTH_SHORT).show();
mHandler.post(new Runnable() {
@Override
public void run() {
SystemClock.sleep(100);
listView.onRefreshComplete();
}
});
return;
}
newsPage++;
loadNews(mMovieGenres, newsPage);
}
});
}
private void loadNews(String newsType, int newsPage) {
Map<String, String> params = new HashMap<>();
params.put("showapi_appid", UrlTool.appid);
params.put("showapi_sign", UrlTool.secret);
params.put("channelId", "");
params.put("channelName", "");
params.put("title", newsType);
params.put("page", "" + newsPage);
params.put("needContent", "0");
params.put("needHtml", "0");
params.put("needAllList", "0");
params.put("maxResult", "50");
params.put("id", "");
Retrofit retrofit = new Retrofit
.Builder()
.baseUrl(UrlTool.APP_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
NewsService newsService = retrofit.create(NewsService.class);
newsService.getNewsData(params).subscribeOn(Schedulers.io()).observeOn(AndroidScheduler.mainThread()).subscribe(new Subscriber<ResponseBody>() {
@Override
public void onCompleted() {
listView.onRefreshComplete();
}
@Override
public void onError(Throwable e) {
listView.onRefreshComplete();
}
@Override
public void onNext(ResponseBody responseBody) {
String json = null;
try {
json = new String(responseBody.bytes());
} catch (IOException e) {
e.printStackTrace();
}
handleResult(json);
}
});
}
private void handleResult(final String json) {
mHandler.post(new Runnable() {
@Override
public void run() {
news.clear();
NewBean newBean = new Gson().fromJson(json, NewBean.class);
NewBean.ShowapiResBodyBean.PagebeanBean pagebeanBean = newBean.getShowapi_res_body().getPagebean();
newsPageSize = pagebeanBean.getAllPages();
List<NewBean.ShowapiResBodyBean.PagebeanBean.ContentlistBean> contentlistBeans = newBean.getShowapi_res_body().getPagebean().getContentlist();
for (int i = 0; i < contentlistBeans.size(); i++) {
int images = contentlistBeans.get(i).getImageurls().size();
String urls = null;
if (images != 0) {
for (int a = 0; a < images; a++) {
urls = contentlistBeans.get(i).getImageurls().get(0).getUrl();
}
}
String title = contentlistBeans.get(i).getTitle();
String link = contentlistBeans.get(i).getLink();
String source = contentlistBeans.get(i).getSource();
String time = contentlistBeans.get(i).getPubDate();
String imageUrl = null;
if (images == 0) {
imageUrl = "null_image_url";
} else {
imageUrl = urls;
}
NewContentBean contentBean = new NewContentBean();
contentBean.setTitle(title);
contentBean.setUrl(link);
contentBean.setSource(source);
contentBean.setTime(time);
contentBean.setImagUrl(imageUrl);
news.add(contentBean);
}
listView.getRefreshableView().setSelection(1); //回到第一行item
adapter.notifyDataSetInvalidated();
}
});
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
startWebActivity(news.get(i - 1).getUrl());
}
private void startWebActivity(String url) {
Intent intent = new Intent(mContext, WebActivity.class);
intent.putExtra("url", url);
startActivity(intent);
}
}
package com.fly.kuaixun.mode;
import java.util.Map;
import okhttp3.ResponseBody;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import rx.Observable;
public interface NewsService {
@FormUrlEncoded
@POST("109-35")
Observable<ResponseBody> getNewsData(@FieldMap Map<String, String> params);
}
以上三部分代码 主要就是 Retrofit +Rxjava + Gson ,以及下拉刷新的主要代码。
代码比较多,就不一一贴代码了,项目内容也是比较清晰,容易看懂的,关于极光推送的相关内容也在项目中。
本项目会一直抽空不定期更新,偶尔会加一些新的功能。
demo 源代码 在这里下载 https://github.com/mapeifan/NewsClient