美文网首页
Android Gank.io客户端

Android Gank.io客户端

作者: 付亮 | 来源:发表于2017-06-20 18:14 被阅读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

    相关文章

      网友评论

          本文标题:Android Gank.io客户端

          本文链接:https://www.haomeiwen.com/subject/jjhaqxtx.html