Android Gank.io客户端

2017-06-20  本文已影响111人  付亮

一直想写一个类似于新闻客户端的APP,但是没有碰到免费的API。不知道什么时候在网上兜兜装转看到了gank.io有开放的API,立马着手准备写一个客户端。

一、客户端设计

主要框架:MVC、RxJavaRetrifit
使用MVC主要是因为整体APP体量较小,没有较多的业务逻辑,没有明显的模块化。网络请求选择Retrifit与RxJava相结合的写法,之前做项目一直都在这样用,不过RxJava没有使用2.0以后的版本。

二、产品设计

整理布局使用的类似DrawerLayout的SlidingRootNav,左侧为RecyclerView列表,显示所有分类,这里SlidingRootNav我稍微改了一下,用户选择分类的时候就把左侧隐藏掉,回到Activity的页面。数据列表是在Fragment中,根据用户选择的分类不同在Activity中加载不同Fragment。列表这里使用的都是RecyclerView,福利部分为瀑布流的形式展示。用户点击item跳转的WebViewActivity,可以复制链接,分享(第一版没有添加功能),收藏。在福利分类中,点击item会到一个图片预览的Fragment中,可以保存图片到相册中。

三、关键部分代码

***********************************网络请求处理************************************
public class InterfaceAPI {

    private InterfaceService interfaceService;

    public InterfaceAPI() {
        interfaceService = new RetrofitClient().getInterfaceService();
    }


    public Observable<ResponseInfo> getAllData(String type,String pageSize ,String page) {
        return interfaceService.getAllInfo(type,pageSize,page).onErrorResumeNext(new Func1<Throwable, Observable<? extends ResponseInfo>>() {
            @Override
            public Observable<? extends ResponseInfo> call(Throwable throwable) {
                return Observable.error(RxHttpHelper.convertIOEError(throwable));
            }
        }).flatMap(new Func1<ResponseInfo, Observable<ResponseInfo>>() {
            @Override
            public Observable<ResponseInfo> call(ResponseInfo responseInfo) {
                if (responseInfo == null) {
                    return Observable.error(new RequestErrorThrowable(HttpErrorInfo.CODE_OF_PARSE_REQUEST_FAILURE,
                            HttpErrorInfo.MSG_OF_PARSE_REQUEST_FAILURE));
                }else {
                    if (responseInfo.isError){
                        return Observable.error(new RequestErrorThrowable("-1", "获取失败"));
                    }else {
                        return Observable.just(responseInfo);
                    }
                }
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }
}
***********************************发送网络请求************************************
public interface InterfaceService {

    @GET("{type}/{pageSize}/{page}")
    Observable<ResponseInfo> getAllInfo(@Path("type") String type,@Path("pageSize")String pageSize, @Path("page")String page);
}
**************************************首页***************************************
public class SampleActivity extends AppCompatActivity{

    private String[] screenTitles;

    private SlidingRootNavBuilder builder;
    private WelfareFragment welfareFragment;
    private AllListFragment allListFragment;
    private CollectFragment collectFragment;
    private MenuAdapter adapter;
    private long exitTime = 0;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setTitleTextColor(ContextCompat.getColor(this,R.color.white));
        setSupportActionBar(toolbar);
        builder = new SlidingRootNavBuilder(this);
        builder .withToolbarMenuToggle(toolbar)
                .withMenuOpened(false)
                .withSavedState(savedInstanceState)
                .withMenuLayout(R.layout.menu_left_drawer)
                .inject();
        screenTitles = loadTitleString();

        adapter = new MenuAdapter(this);
        adapter.setOnItemClickListener(new MenuAdapter.ItemClickListener() {
            @Override
            public void onItemClick(int position) {
                adapter.setSelected(position);
                show(position);
            }
        });
        RecyclerView list = (RecyclerView) findViewById(R.id.list);
        list.setNestedScrollingEnabled(false);
        list.setLayoutManager(new LinearLayoutManager(this));
        list.setAdapter(adapter);
        show(0);
        LinearLayout about = (LinearLayout)findViewById(R.id.about_layout);
        about.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showDialog();
            }
        });
    }

    @Override
    public void onBackPressed() {
        if ((System.currentTimeMillis() - exitTime) > 2000) {
            Snackbar.make(getWindow().getDecorView(),"再按一次退出", BaseTransientBottomBar.LENGTH_SHORT).show();
            exitTime = System.currentTimeMillis();
        } else {
            quit();
        }
    }

    protected void quit() {
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        startActivity(intent);
        moveTaskToBack(true);
        finish();
    }

    private void showDialog(){
        MaterialDialog.Builder builder = new MaterialDialog.Builder(this);
        builder.title("About");
        builder.negativeText("关闭");
        builder.negativeColorRes(R.color.colorAccent);
        builder.content(R.string.about);
        builder.onNegative(new MaterialDialog.SingleButtonCallback() {
            @Override
            public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                dialog.dismiss();
            }
        });
        builder.build().show();
    }

    public void show(int position){
        adapter.setSelected(position);
        Fragment selectedScreen;
        if (position == 5){
            if (welfareFragment == null){
                welfareFragment = new WelfareFragment();
            }
            selectedScreen = welfareFragment;
        }else if (position == 7){
            if (collectFragment == null){
                collectFragment = new CollectFragment();
            }
            selectedScreen = collectFragment;
        }
        else {
            if (allListFragment == null) {
                allListFragment = new AllListFragment();
            }
            selectedScreen = allListFragment;
            if (allListFragment.isAdded()){
                allListFragment.setType(screenTitles[position]);
            }else {
                Bundle bundle = new Bundle();
                bundle.putString("type",screenTitles[position]);
                allListFragment.setArguments(bundle);
            }
        }
        if (getSupportActionBar() != null){
            if (position == 0){
                getSupportActionBar().setTitle(getString(R.string.app_name));
            }else {
                getSupportActionBar().setTitle(screenTitles[position]);
            }
        }
        builder.hideMenu();
        showFragment(selectedScreen);
    }

    private void showFragment(Fragment fragment) {
        getFragmentManager().beginTransaction()
                .replace(R.id.container, fragment)
                .commit();
    }

    private String[] loadTitleString() {
        return getResources().getStringArray(R.array.title_list);
    }

}
**********************************自定义application********************************
//主要处理缓存,两次启动间隔12小时,会把除了“我的收藏”模块的其他缓存数据清除,这里也不包括图片缓存。

public class MyApplication extends Application{

    private String [] title;

    @Override
    public void onCreate() {
        super.onCreate();
        DataCache.instance.init(MyApplication.this);
        Long saveTime = DataCache.instance.getCacheData("fuliang","limitTime");
        Long nowTime = System.currentTimeMillis();
        if (saveTime == null){
            DataCache.instance.saveCacheData("fuliang","limitTime",nowTime);
        }else {
            title = getResources().getStringArray(R.array.ld_activityScreenTitles);
            int i = 0;
            if (nowTime - saveTime > 12*60*60*1000){
                while (i<title.length-1){
                    DataCache.instance.clearCacheData("fuliang",title[i]);
                    i++;
                }
            }
        }
    }
}
************************************列表适配器************************************
/**
 * Created by lfu on 2017/6/8.
 */

public class AllListFragment extends Fragment implements WaveSwipeRefreshLayout.OnRefreshListener{

    private WaveSwipeRefreshLayout swipeLayout;
    private RecyclerView recyclerView;
    private AllDataAdapter allDataAdapter;
    private ArrayList<ResultsList> allDataList;
    private String itemType;
    private LoadingFragment loadingFragment;
    private Animator spruceAnimator;
    private int page = 1;
    private boolean isLoadMore = false;
    private boolean isHaveMore = true;

    @Nullable
    @Override
    public View onCreateView(final LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.all_fragment_layout,container,false);
        recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new MyLayoutManager(getActivity()){
            @Override
            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
                super.onLayoutChildren(recycler, state);
                spruceAnimator = new Spruce.SpruceBuilder(recyclerView)
                        .sortWith(new DefaultSort(100))
                        .animateWith(DefaultAnimations.shrinkAnimator(recyclerView, 800),
                                ObjectAnimator.ofFloat(recyclerView, "translationX", -recyclerView.getWidth(), 0f).setDuration(800))
                        .start();
            }
        };
        recyclerView.setLayoutManager(layoutManager);
        swipeLayout = (WaveSwipeRefreshLayout)view.findViewById(R.id.swipe_layout);
        swipeLayout.setColorSchemeColors(ContextCompat.getColor(getActivity(),R.color.white),ContextCompat.getColor(getActivity(),R.color.white));
        swipeLayout.setOnRefreshListener(this);
        swipeLayout.setWaveColor(ContextCompat.getColor(getActivity(),R.color.blue));
        allDataAdapter = new AllDataAdapter(getActivity());
        allDataAdapter.setReloadListener(new AllDataAdapter.ReloadListener() {
            @Override
            public void onReload() {
                page++;
                isLoadMore = true;
                getDataFromInternet();
                allDataAdapter.startReload();
            }
        });
        allDataAdapter.setOnItemClickListener(new AllDataAdapter.ItemClickListener() {
            @Override
            public void onItemClick(ResultsList model) {
                Intent intent = new Intent(getActivity(), WebActivity.class);
                intent.putExtra("model",model);
                startActivity(intent);
            }
        });
        recyclerView.addOnScrollListener(new EndLessOnScrollListener(layoutManager) {
            @Override
            public void onLoadMore() {
                if (isHaveMore){
                    page++;
                    isLoadMore = true;
                    getDataFromInternet();
                }
            }
        });
        itemType = getArguments().getString("type");
        setType(itemType);
        return view;
    }


    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    private void getDataFromInternet(){
        BusinessHelper.getAllData(itemType,"30",String.valueOf(page)).subscribe(new Action1<ResponseInfo>() {
            @Override
            public void call(ResponseInfo responseInfo) {
                if (swipeLayout.isRefreshing()) {
                    swipeLayout.setRefreshing(false);
                }
                if (loadingFragment != null){
                    loadingFragment.removeSelf(getFragmentManager());
                }
                if (responseInfo.results.size() < 30){
                    isHaveMore = false;
                }

                if (page == 1){
                    DataCache.instance.saveCacheData("fuliang", TypeHelper.getType(itemType),responseInfo.results);
                }
                allDataList = responseInfo.results;
                setViewData(isLoadMore);

                if (spruceAnimator != null){
                    spruceAnimator.start();
                }

            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                if (loadingFragment != null){
                    loadingFragment.loadFail();
                }
                if (isLoadMore){
                    page--;
                    isHaveMore = true;
                    allDataAdapter.loadMoreFail();
                }
            }
        });
    }

    private void setViewData(boolean isLoadMore){
        if (isLoadMore){
            allDataAdapter.addData(allDataList);
        }else {
            allDataAdapter.setData(allDataList);
        }
        if (recyclerView.getAdapter() == null){
            recyclerView.setAdapter(allDataAdapter);
        }else {
            if (!isLoadMore){
                recyclerView.scrollTo(0,0);
            }
            allDataAdapter.notifyDataSetChanged();
        }

    }

    public void setType(String type){
        itemType = type;
        isLoadMore = false;
        page = 1;
        if (recyclerView != null){
            recyclerView.removeAllViews();
            if (allDataList != null){
                allDataList.clear();
                allDataAdapter.notifyDataSetChanged();
            }
        }
        if (loadingFragment != null){
            loadingFragment.removeSelf(getFragmentManager());
        }
        allDataList = DataCache.instance.getCacheData("fuliang",TypeHelper.getType(type));
        if (allDataList == null){
            if (!type.equals("我的收藏")){
                showFragment();
                getDataFromInternet();
            }else {
                showFragment();
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        loadingFragment.noData();
                    }
                },500);
            }
            return;
        }
        if (allDataList.size()>0){
            setViewData(isLoadMore);
            if (spruceAnimator != null){
                spruceAnimator.start();
            }
        }else {
            showFragment();
            getDataFromInternet();
        }
    }

    private void showFragment() {
        loadingFragment= new LoadingFragment();
        loadingFragment.setReloadListener(new LoadingFragment.ReloadData() {
            @Override
            public void reloadData() {
                loadingFragment.reloadData();
                page = 1;
                getDataFromInternet();
            }
        });
        getFragmentManager().beginTransaction()
                .replace(R.id.loading_layout, loadingFragment)
                .commitAllowingStateLoss();
    }

    @Override
    public void onRefresh() {
        page = 1;
        getDataFromInternet();
    }
}

四、使用到的第三方类库

    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support:design:25.3.1'
    compile 'com.android.support:recyclerview-v7:25.3.1'
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
    compile 'com.android.support:cardview-v7:25.3.1'
    compile 'com.github.recruit-lifestyle:WaveSwipeRefreshLayout:1.6'
    compile 'com.afollestad.material-dialogs:core:0.9.4.5'
    compile 'com.afollestad.material-dialogs:commons:0.9.4.5'
    compile 'com.squareup.picasso:picasso:2.5.2'
    compile 'com.android.support:support-v4:25.3.1'
    compile 'com.willowtreeapps.spruce:spruce-android:1.0.1'
    compile 'com.oguzdev:CircularFloatingActionMenu:1.0.2'

五、截图

首页
首页_2.jpg
福利.jpg
ios.jpg

六、项目地址

https://github.com/CFuLiang/Gank

上一篇下一篇

猜你喜欢

热点阅读