一、案例效果图
二、技能描述
通过Retrofit进行网络请求数据,要求后两个参数(下方接口地址中的10和1)通过注解方式传进去,
使用RecyclerView进行列表展示,请求到数据之后,使用GreenDao进行数据缓存,当进入页面时,
先从数据库中取数据,进行展示,再进行网络请求,有数据就更新,没有网络情况下只显示数据库中数据。
进入页面后判断网络状态,通过EventBus发送网络状态并Toast显示,使用ButterKnife进行视图注入,
使用Fresco进行列表图片展示。
接口地址
http://gank.io/api/data/Android/10/1
三、实现思路
1. 实现整体页面布局,下方5个底部菜单选项,上方添加5个Fragment
2. 导入Fresco,EventBus,GreenDao,Butterknife,Retrofit等的依赖
3. 进入页面后进行网络状态判断,判断后把状态通过EventBus进行发送并Toast显示
4. 创建试题类型,并通过GreenDao生成相应的表结构,进入页面后,进行数据库查询,
将查询到到的数据保存到List中
5. 使用RecyclerView进行列表展示,为RecyclerView创建适配器,使用ButterKnife为ViewHolder注入视图,
重写适配器方法,通过Fresco展示图片
6. 为RecyclerView设置适配器,当有网络时进行网络请求,初始化Retrofit对象,创建接口类,通过注解的方式
设置参数,执行网络请求自动解析数据,如果数据不为空的情况则更新List并更新数据库,刷新列表数据,
7. 断开网络,重新进入,测试数据库缓存是否生效
添加依赖
添加greendao的依赖需要先在APP下的build.gradle进行配置
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
mavenCentral()//加入greendao
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'//加入greendao
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
在项目的build.gradle中添加依赖
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'//加入greendao
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.bwie.com.oneweekdemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' //Retrofit2所需要的包
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' //ConverterFactory的Gson依赖包
compile'org.greenrobot:greendao:3.2.2'//greendao
compile'org.greenrobot:greendao-generator:3.2.2'//greendao
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'//recyclerview
compile 'com.hjm:BottomTabBar:1.1.1'//底部导航栏
compile 'org.greenrobot:eventbus:3.1.1'//enentbus
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'//butterknife
compile 'com.facebook.fresco:fresco:0.12.0'//fresco
}
//加入greendao
greendao {
schemaVersion 1
daoPackage 'com.bwie.com.oneweekdemo.gen'
targetGenDir 'src/main/java'
}
网络权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
ResultsBean(参考)
@Entity
public class ResultsBean {
@Id
private String _id;
private String createdAt;
private String desc;
private String publishedAt;
private String source;
private String type;
private String url;
private boolean used;
private String who;
@Convert(columnType = String.class, converter = StringConverter.class)
private List<String> images;
User中有一种List类型的数据也需要保存到数据库中,该如何处理呢?
答案很简单,可以转换一下思路,遍历List数据,然后将所有的String对象都append到一个StringBuilder中,
然后保存在数据库中即可。事实上,GreenDao已经为我们考虑到了这种情况,因此才有了 PropertyConverter
这个接口
StringConverter
/**
* Created by Wangrx on 2017/12/2.
*/
public class StringConverter implements PropertyConverter<List<String>, String> {
@Override
public List<String> convertToEntityProperty(String databaseValue) {
if (databaseValue == null) {
return null;
}
else {
List<String> list = Arrays.asList(databaseValue.split(","));
return list;
}
}
@Override
public String convertToDatabaseValue(List<String> entityProperty) {
if(entityProperty==null){
return null;
}
else{
StringBuilder sb= new StringBuilder();
for(String link:entityProperty){
sb.append(link);
sb.append(",");
}
return sb.toString();
}
}
}
BlogService接口定义
/**
* Created by Wangrx on 2017/12/2.
*/
public interface BlogService {
@GET("10/1")
Call<JavaBean> getUrl();
}
RetrofitUtils(Retrofit封装)/**
* Created by Wangrx on 2017/12/2.
*/
public class RetrofitUtils {
private static volatile RetrofitUtils instance;
private Retrofit retrofit;
private RetrofitUtils(){
}
private RetrofitUtils(String baseUrl){
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
.build();
}
public static RetrofitUtils getInstance(String baseUrl){
if (instance==null){
synchronized (RetrofitUtils.class){
if (null==instance){
instance = new RetrofitUtils(baseUrl);
}
}
}
return instance;
}
public static RetrofitUtils getInstance(){
if (null == instance){
return getInstance("http://gank.io/api/data/Android/");
}
return instance;
}
public Retrofit getRetrofit(){
return retrofit;
}
}
DBUtils(greendao封装)/**
* Created by Wangrx on 2017/12/2.
*/
public class DBUtils {
private static volatile DBUtils instance;
private final ResultsBeanDao dao;
public DBUtils(Context context){
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, "resultsbean.db", null);
SQLiteDatabase database = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(database);
DaoSession daoSession = daoMaster.newSession();
dao = daoSession.getResultsBeanDao();
}
public static DBUtils getInstance(Context context){
if (null==instance){
synchronized (DBUtils.class){
if (null==instance){
instance = new DBUtils(context);
}
}
}
return instance;
}
public ResultsBeanDao getDao(){
return dao;
}
}
BaseApplication(存放本地数据,继承Application,初始化Fresco)/**
* Created by Wangrx on 2017/12/2.
*/
public class BaseApplication extends Application{
private static BaseApplication instance;
private List<ResultsBean> list;
@Override
public void onCreate() {
super.onCreate();
instance=this;
Fresco.initialize(this);//初始化Fresco类
}
public static BaseApplication getInstance(){
return instance;
}
public List<ResultsBean> getData(){
if (list==null){
list=new ArrayList<>();
}
return list;
}
public void setData(List<ResultsBean> list){
this.list = list;
}
}
MessageEvent(EventBus定义事件的类)/**
* Created by Wangrx on 2017/12/2.
*/
public class MessageEvent {
private boolean connected;
public boolean isConnected() {
return connected;
}
public void setConnected(boolean connected) {
this.connected = connected;
}
}
MainActivitypublic class MainActivity extends AppCompatActivity {
private BottomTabBar mb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册
EventBus.getDefault().register(this);
mb = (BottomTabBar) findViewById(R.id.bottom_tab_bar);
mb.init(getSupportFragmentManager())
.setImgSize(50, 50)
.setFontSize(8)
.setTabPadding(4, 6, 10)
.setChangeColor(Color.RED, Color.DKGRAY)
.addTabItem("首页", R.mipmap.shouye, FragmentShow.class)
.addTabItem("分类", R.mipmap.fenlei, FragmentFen.class)
.addTabItem("优惠", R.mipmap.youhui, FragmentYou.class)
.addTabItem("购物车", R.mipmap.gouwuche, FragmentCart.class)
.addTabItem("我的", R.mipmap.people, FragmentMy.class)
.isShowDivider(true)
.setOnTabChangeListener(new BottomTabBar.OnTabChangeListener() {
@Override
public void onTabChange(int position, String name) {
}
});
}
@Subscribe()
public void getConnected(MessageEvent event){
if (event.isConnected()){
Toast.makeText(this, "当前网络状态良好", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "当前网络不可用", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
FragmentShow/**
* Created by Wangrx on 2017/12/2.
*/
public class FragmentShow extends Fragment{
private Retrofit retrofit;
private ResultsBeanDao dao;
private List<ResultsBean> data;
private RecyclerView recyView;
private MyAdapter adapter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = View.inflate(getContext(), R.layout.fragmertshow, null);
recyView = view.findViewById(R.id.recy_view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
dao = DBUtils.getInstance(getContext()).getDao();
boolean connected = isNetworkConnected(getActivity());
Log.i("zzz", "onCreate: "+connected);
MessageEvent event = new MessageEvent();
event.setConnected(connected);
EventBus.getDefault().post(event);
initView();
}
private void initView() {
data = BaseApplication.getInstance().getData();
LinearLayoutManager manager = new LinearLayoutManager(getContext());
recyView.setLayoutManager(manager);
adapter = new MyAdapter(getContext(), data);
recyView.setAdapter(adapter);
if (data == null || data.size() == 0) {
Log.i("zzz", "内存中无数据");
//从数据库中取
List<ResultsBean> dataFromDB = getDataFromDB();
//数据库中也是空
if (dataFromDB==null||dataFromDB.size()==0){
Log.i("zzz", "数据控中为空");
//从网络中取数据
getDataFromNet();
}else {
Log.i("zzz", "数据库中不为空");
putDataToMemory(dataFromDB);
}
}
if (data==null||data.size()==0){
Log.i("zzz", "内存中无数据");
List<ResultsBean> dataFromDB = getDataFromDB();
if (dataFromDB==null||dataFromDB.size()==0){
Log.i("zzz", "数据库中为空");
getDataFromNet();
}else {
putDataToMemory(dataFromDB);
}
}
}
public void getDataFromNet(){
retrofit = RetrofitUtils.getInstance().getRetrofit();
BlogService service = retrofit.create(BlogService.class);
Call<JavaBean> call = service.getUrl();
call.enqueue(new Callback<JavaBean>() {
@Override
public void onResponse(Call<JavaBean> call, Response<JavaBean> response) {
List<ResultsBean> results = response.body().getResults();
Log.i("zzz", "onResponse: "+data.toString());
if (results!=null){
Log.i("zzz", "网络不为空");
putDataToMemory(results);
for (ResultsBean databean:results){
dao.insertOrReplace(databean);
}
}
}
@Override
public void onFailure(Call<JavaBean> call, Throwable t) {
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
private void putDataToMemory(List<ResultsBean> list) {
Log.i("zzz", "向内存中存数据");
data.clear();
data.addAll(list);
adapter.notifyDataSetChanged();
BaseApplication.getInstance().setData(data);
}
//从数据库中取数据
public List<ResultsBean> getDataFromDB(){
Log.i("zzz", "从数据库中取");
//查询所有数据
List<ResultsBean> dataBeen = dao.loadAll();
if (dataBeen==null||dataBeen.size()==0){
Log.i("zzz", "null");
}else {
Log.i("zzz", "getDataFromDB: "+dataBeen.size());
}
return dataBeen;
}
public boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
}
MyAdapter(多条目加载)/**
* Created by Wangrx on 2017/12/2.
*/
public class MyAdapter extends RecyclerView.Adapter {
public static final int TYPE_ONE_IMAGE = 0;
public static final int TYPE_TWO_IMAGE = 1;
private Context context;
private List<ResultsBean> list;
public MyAdapter(Context context, List<ResultsBean> list) {
this.context = context;
this.list = list;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
RecyclerView.ViewHolder holder = null;
switch (viewType) {
case 0:
view = View.inflate(context, R.layout.item, null);
holder = new OneViewHolder(view);
break;
case 1:
view = View.inflate(context, R.layout.twoitem, null);
holder = new TwoViewHolder(view);
}
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case 0:
OneViewHolder holderOne = (OneViewHolder) holder;
holderOne.itemDesc.setText(list.get(position).getDesc());
holderOne.itemType.setText(list.get(position).getType());
break;
case 1:
TwoViewHolder holdertwo = (TwoViewHolder) holder;
List<String> images = list.get(position).getImages();
if (images != null && images.size() != 0) {
// Glide.with(context).load(images.get(0)).into(holdertwo.itemImg);
holdertwo.itemImage.setImageURI(images.get(0));
}
holdertwo.itemDesc.setText(list.get(position).getDesc());
holdertwo.itemType.setText(list.get(position).getType());
}
}
@Override
public int getItemViewType(int position) {
List<String> images = list.get(position).getImages();
if (images != null && images.size() != 0) {
return TYPE_TWO_IMAGE;
} else {
return TYPE_ONE_IMAGE;
}
}
@Override
public int getItemCount() {
return list.size();
}
public class OneViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.item_desc)
TextView itemDesc;
@BindView(R.id.item_type)
TextView itemType;
public OneViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
public class TwoViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.item_image)
SimpleDraweeView itemImage;
@BindView(R.id.item_desc)
TextView itemDesc;
@BindView(R.id.item_type)
TextView itemType;
public TwoViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context="com.bwie.com.oneweekdemo.MainActivity">
<com.hjm.bottomtabbar.BottomTabBar
android:id="@+id/bottom_tab_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</com.hjm.bottomtabbar.BottomTabBar>
</RelativeLayout>