新增通用列表模块,问答精选

This commit is contained in:
kehaoyuan
2017-12-04 18:47:53 +08:00
parent d537cb2383
commit 3978ab95f4
31 changed files with 944 additions and 692 deletions

View File

@ -24,4 +24,12 @@ public class ItemViewType {
public static final int KC_HINT = 17;
public static final int GAME_PULGIN = 18; // 游戏插件模块
/**
* 普通列表
*/
public static final int ITEM_BODY = 100;
public static final int ITEM_FOOTER = 101;
public static final int ITEM_TOP = 102;
}

View File

@ -15,13 +15,13 @@ public class VerticalItemDecoration extends RecyclerView.ItemDecoration {
private Paint paint;
private int mInterval = 0;
private boolean isShowIntervalTop;
private boolean mIsShowIntervalTop;
public VerticalItemDecoration(Context context, int interval, boolean isShowIntervalTop) {
paint = new Paint();
paint.setColor(ContextCompat.getColor(context, R.color.background));
mInterval = DisplayUtils.dip2px(context, interval);
this.isShowIntervalTop = isShowIntervalTop;
mIsShowIntervalTop = isShowIntervalTop;
}
@Override
@ -35,7 +35,7 @@ public class VerticalItemDecoration extends RecyclerView.ItemDecoration {
int top = child.getBottom() + layoutParams.bottomMargin;
int bottom = top + mInterval;
if (i == 0 && isShowIntervalTop) {
if (i == 0 && mIsShowIntervalTop) {
c.drawRect(left, 0, right, bottom, paint);
} else {
c.drawRect(left, top, right, bottom, paint);
@ -47,7 +47,7 @@ public class VerticalItemDecoration extends RecyclerView.ItemDecoration {
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
int position = parent.getChildPosition(view);
if (position == 0 && isShowIntervalTop) {
if (position == 0 && mIsShowIntervalTop) {
outRect.set(0, mInterval, 0, mInterval);
} else {
outRect.set(0, 0, 0, mInterval);

View File

@ -6,6 +6,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.base.OnListClickListener;
import com.gh.gamecenter.R;
import butterknife.BindView;
@ -35,11 +36,36 @@ public class FooterViewHolder extends BaseRecyclerViewHolder {
super(itemView);
}
public FooterViewHolder(View itemView, Object data, OnListClickListener listClickListener) {
super(itemView, data, listClickListener);
itemView.setOnClickListener(this);
}
// 减去recyclerView item间隔个别列表间隔很大
public void initItemPadding() {
itemView.setPadding(0, 0, 0, 0);
}
public void initFooterViewHolder(boolean isNetworkError, boolean isOver) {
if (isNetworkError) {
lineLeft.setVisibility(View.GONE);
lineRight.setVisibility(View.GONE);
loading.setVisibility(View.GONE);
hint.setText(R.string.loading_failed_retry);
} else if (isOver) {
lineLeft.setVisibility(View.VISIBLE);
lineRight.setVisibility(View.VISIBLE);
loading.setVisibility(View.GONE);
hint.setText(R.string.loading_complete);
} else {
lineLeft.setVisibility(View.GONE);
lineRight.setVisibility(View.GONE);
loading.setVisibility(View.VISIBLE);
hint.setText(R.string.loading);
}
}
public void initFooterViewHolder(boolean mIsNetworkError, boolean mIsOver, OnClickListener onClickListener) {
if (mIsNetworkError) {
lineLeft.setVisibility(View.GONE);

View File

@ -0,0 +1,93 @@
package com.gh.gamecenter.ask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.View;
import android.widget.CheckedTextView;
import android.widget.ImageView;
import android.widget.TextView;
import com.gh.base.adapter.FragmentAdapter;
import com.gh.base.fragment.BaseFragment;
import com.gh.gamecenter.R;
import com.lightgame.view.NoScrollableViewPager;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.OnClick;
/**
* Created by khy on 2/12/17.
*/
public class AskFragment extends BaseFragment {
@BindView(R.id.ask_selectgame)
ImageView mAskSelectgame;
@BindView(R.id.ask_gamename)
TextView mAskGamename;
@BindView(R.id.ask_search)
ImageView mAskSearch;
@BindView(R.id.ask_hot)
CheckedTextView mAskHot;
@BindView(R.id.ask_questions)
CheckedTextView mAskQuestions;
@BindView(R.id.ask_viewpager)
NoScrollableViewPager mAskViewpager;
public static final int INDEX_HOT = 0;
public static final int INDEX_QUESTIONS = 1;
@Override
protected int getLayoutId() {
return R.layout.fragment_ask;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initViewPager();
setTabbarPosition(INDEX_HOT);
}
@OnClick({R.id.ask_selectgame, R.id.ask_search, R.id.ask_hot, R.id.ask_questions})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.ask_selectgame:
break;
case R.id.ask_search:
break;
case R.id.ask_hot:
setTabbarPosition(INDEX_HOT);
break;
case R.id.ask_questions:
setTabbarPosition(INDEX_QUESTIONS);
break;
}
}
private void initViewPager() {
ArrayList<Fragment> fragmentList = new ArrayList<>();
fragmentList.add(new AskHotFragment());
fragmentList.add(new AskQuestionsFragment());
mAskViewpager.setAdapter(new FragmentAdapter(getChildFragmentManager(), fragmentList));
mAskViewpager.setScrollable(false);
}
private void setTabbarPosition(int index) {
switch (index) {
case INDEX_HOT:
mAskHot.setChecked(true);
mAskQuestions.setChecked(false);
break;
case INDEX_QUESTIONS:
mAskHot.setChecked(false);
mAskQuestions.setChecked(true);
break;
}
mAskViewpager.setCurrentItem(index);
}
}

View File

@ -0,0 +1,98 @@
package com.gh.gamecenter.ask;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import com.gh.base.OnListClickListener;
import com.gh.common.constant.ItemViewType;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder;
import com.gh.gamecenter.baselist.BaseListAdapter;
import com.gh.gamecenter.baselist.LoadStatus;
import com.gh.gamecenter.entity.NewsEntity;
import java.util.List;
/**
* Created by khy on 2/12/17.
*/
public class AskHotAdapter extends BaseListAdapter {
private OnListClickListener mListClickListener;
private List<NewsEntity> mEntityList;
public AskHotAdapter(Context context, OnListClickListener listClickListener) {
super(context);
mListClickListener = listClickListener;
}
@NonNull
@Override
protected <T> void provideListData(List<T> listData) {
mEntityList = (List<NewsEntity>) listData;
notifyDataSetChanged();
}
@Override
protected void loadChange(LoadStatus status) {
switch (status) {
case over:
mIsOver = true;
break;
case error:
mIsNetworkError = true;
break;
case retry:
mIsNetworkError = false;
break;
}
notifyItemChanged(getItemCount() - 1);
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1) return ItemViewType.ITEM_FOOTER;
return ItemViewType.ITEM_BODY;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case ItemViewType.ITEM_FOOTER:
view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false);
return new FooterViewHolder(view, null, mListClickListener);
case ItemViewType.ITEM_BODY:
view = mLayoutInflater.inflate(R.layout.ask_item, parent, false);
return new AskItemViewHolder(view, mEntityList, mListClickListener);
default:
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ItemViewType.ITEM_BODY:
NewsEntity newsEntity = mEntityList.get(position);
AskItemViewHolder askHolder = (AskItemViewHolder) holder;
askHolder.mAskUsername.setText(newsEntity.getTitle());
break;
case ItemViewType.ITEM_FOOTER:
FooterViewHolder footerViewHolder = (FooterViewHolder) holder;
footerViewHolder.initItemPadding();
footerViewHolder.initFooterViewHolder(mIsNetworkError, mIsOver);
break;
}
}
@Override
public int getItemCount() {
return mEntityList == null || mEntityList.isEmpty() ? 0 : mEntityList.size() + FOOTER_ITEM_COUNT;
}
}

View File

@ -0,0 +1,48 @@
package com.gh.gamecenter.ask;
import android.view.View;
import com.gh.gamecenter.R;
import com.gh.gamecenter.baselist.BaseListAdapter;
import com.gh.gamecenter.baselist.ListFragment;
import com.gh.gamecenter.baselist.LoadStatus;
import com.gh.gamecenter.baselist.LoadType;
import com.gh.gamecenter.entity.NewsEntity;
import com.gh.gamecenter.retrofit.RetrofitManager;
import java.util.List;
import rx.Observable;
/**
* Created by khy on 2/12/17.
*/
public class AskHotFragment extends ListFragment {
private AskHotAdapter mAdapter;
@SuppressWarnings("unchecked")
@Override
public Observable<List<NewsEntity>> provideDataObservable() {
return RetrofitManager.getInstance(getContext()).getApi().getZiXun(getListOffset());
}
@Override
protected BaseListAdapter provideListAdapter() {
return mAdapter == null ? mAdapter = new AskHotAdapter(getContext(), this) : mAdapter;
}
@Override
public void onListClick(View view, int position, Object data) {
switch (view.getId()) {
case R.id.footerview_item:
if (mAdapter.isNetworkError()) {
mAdapter.loadChange(LoadStatus.retry);
mListViewModel.load(LoadType.retry);
}
break;
}
}
}

View File

@ -0,0 +1,37 @@
package com.gh.gamecenter.ask;
import android.view.View;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.base.OnListClickListener;
import com.gh.gamecenter.R;
import butterknife.BindView;
/**
* Created by khy on 4/12/17.
*/
public class AskItemViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.ask_item_usericon)
SimpleDraweeView mAskUsericon;
@BindView(R.id.ask_item_username)
TextView mAskUsername;
@BindView(R.id.ask_item_title)
TextView mAskTitle;
@BindView(R.id.ask_item_content)
TextView mAskContent;
@BindView(R.id.ask_item_img)
SimpleDraweeView mAskImg;
@BindView(R.id.ask_item_votecount)
TextView mAskVotecount;
public AskItemViewHolder(View itemView, Object data, OnListClickListener listClickListener) {
super(itemView, data, listClickListener);
itemView.setOnClickListener(this);
}
}

View File

@ -0,0 +1,15 @@
package com.gh.gamecenter.ask;
import com.gh.base.fragment.BaseFragment;
import com.gh.gamecenter.R;
/**
* Created by khy on 2/12/17.
*/
public class AskQuestionsFragment extends BaseFragment {
@Override
protected int getLayoutId() {
return R.layout.game_normal_item;
}
}

View File

@ -12,13 +12,21 @@ import java.util.List;
public abstract class BaseListAdapter extends BaseRecyclerAdapter {
protected static final int FOOTER_ITEM_COUNT = 1;
protected boolean mIsOver;
protected boolean mIsNetworkError;
public BaseListAdapter(Context context) {
super(context);
}
protected abstract <T> void provideListData(List<T> listData);
protected abstract void loadError(Throwable e);
protected abstract void loadChange(LoadStatus status);
protected abstract void refreshData();
public boolean isNetworkError() {
return mIsNetworkError;
}
}

View File

@ -1,68 +0,0 @@
package com.gh.gamecenter.baselist;
import android.view.View;
import java.util.List;
import rx.Observable;
/**
* Created by khy on 6/11/17.
*/
class BaseListContrat {
interface ListAppView {
/**
* 加载完成(不是结束)
*
* @param dataList 当前加载数据(不是全部)
* @param <T>
*/
<T> void LoadFinish(List<T> dataList);
/**
* 加载失败,注意首次加载和分页加载
*
* @param e
*/
void LoadError(Throwable e);
/**
* 提供数据源头
*
* @param <T>
* @return
*/
<T> Observable<List<T>> provideDataObservable();
/**
* 列表 RecyclerView Adapter
*
* @return
*/
BaseListAdapter provideRecyclerAdapter();
}
interface BaseListPresenter {
/**
* 加载数据, 无论是初始化还是分页都只用一个(注意控制 ListSize-offset)
*/
void load();
/**
* 加载重试
*/
void retryLoad();
/**
* 提供 Fragment View 可以不传, 不传的话自己搞
*
* @param view Fragment View
*/
void provideView(View view);
}
}

View File

@ -1,53 +0,0 @@
package com.gh.gamecenter.baselist;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import com.gh.base.fragment.BaseFragment;
import com.gh.gamecenter.R;
import java.util.List;
/**
* Created by khy on 11/11/17.
*/
public abstract class BaseListFragment extends BaseFragment implements BaseListContrat.ListAppView {
protected BaseListPresenterImpl mListPresenter;
@Override
protected int getLayoutId() {
return R.layout.fragment_list_base;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListPresenter = new BaseListPresenterImpl(this, getContext());
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mListPresenter.provideView(view);
}
protected abstract BaseListAdapter provideAdapter();
@Override
public <T> void LoadFinish(List<T> dataList) {
provideAdapter().provideListData(dataList);
}
@Override
public void LoadError(Throwable e) {
provideAdapter().loadError(e);
}
@Override
public BaseListAdapter provideRecyclerAdapter() {
return provideAdapter();
}
}

View File

@ -1,206 +0,0 @@
package com.gh.gamecenter.baselist;
import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.gh.gamecenter.R;
import com.gh.gamecenter.retrofit.Response;
import com.lightgame.utils.Utils;
import java.util.List;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Created by khy on 6/11/17.
*/
public class BaseListPresenterImpl implements BaseListContrat.BaseListPresenter {
public final static int PAGE_SIZE = 20;
private final static String TAG = "BaseListPresenterImpl";
private Context mContext;
private BaseListContrat.ListAppView mListAppView;
private RecyclerView mListRv;
private SwipeRefreshLayout mRefreshLayout;
private View mLoadingView;
private View mNoConnView;
private View mNoDataView;
private LinearLayoutManager mLayoutManager;
private int mListSize; // 相当于load offset
private boolean mIsLoading;
private boolean mIsNetworkError;
private boolean mIsOver;
public BaseListPresenterImpl(BaseListContrat.ListAppView listAppView, Context context) {
this.mListAppView = listAppView;
this.mContext = context;
this.mListSize = 0;
}
@Override
public void provideView(View view) {
if (view == null) {
Utils.log(TAG, "provideView View 为空, Fragment自己处理");
return;
}
if (mListAppView.provideRecyclerAdapter() == null) {
throw new NullPointerException("recycler is null - 请注意初始化先后顺序");
}
mLayoutManager = new LinearLayoutManager(mContext);
mLoadingView = view.findViewById(R.id.list_loading);
mNoConnView = view.findViewById(R.id.reuse_no_connection);
mNoDataView = view.findViewById(R.id.reuse_none_data);
mRefreshLayout = view.findViewById(R.id.list_refresh);
mRefreshLayout.setColorSchemeResources(R.color.theme);
mListRv = view.findViewById(R.id.list_rv);
mListRv.setLayoutManager(mLayoutManager);
mListRv.setAdapter(mListAppView.provideRecyclerAdapter());
mListRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE
&& mLayoutManager.findLastVisibleItemPosition() + 1 == mLayoutManager.getItemCount()) {
if (!mIsOver && !mIsNetworkError && !mIsLoading) {
load();
}
}
}
});
mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
initListStatus();
mListAppView.provideRecyclerAdapter().refreshData();
load();
}
});
mNoConnView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
initListStatus();
initLoadView(true, false, -1, -1);
load();
}
});
}
@Override
public void load() {
if (mListAppView.provideDataObservable() == null) {
throw new NullPointerException("List data Observable is null");
}
mIsLoading = true;
mListAppView
.provideDataObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<List<Object>>() {
@Override
public void onNext(List<Object> response) {
super.onNext(response);
int size = response.size();
mListAppView.LoadFinish(response);
initLoadView(false, false, mListSize, size);
if (size < PAGE_SIZE) {
mIsOver = true;
}
mIsLoading = false;
mIsNetworkError = false;
mListSize += size;
}
@Override
public void onError(Throwable e) {
super.onError(e);
mIsLoading = false;
mIsNetworkError = true;
mListAppView.LoadError(e);
initLoadView(false, true, mListSize, -1);
}
});
}
@Override
public void retryLoad() {
mIsLoading = false;
mIsOver = false;
mIsNetworkError = false;
load();
}
public int getListOffset() {
return mListSize;
}
private void initListStatus() {
mIsLoading = false;
mIsOver = false;
mIsNetworkError = false;
mListSize = 0;
}
private void initLoadView(boolean isRefresh, boolean isLoadError, int offset, int curListSize) {
if (mLoadingView != null && mNoConnView != null && mNoDataView != null) {
if (isRefresh) { // 下拉刷新
mLoadingView.setVisibility(View.VISIBLE);
mNoDataView.setVisibility(View.GONE);
mNoConnView.setVisibility(View.GONE);
mListRv.setVisibility(View.GONE);
mRefreshLayout.setRefreshing(false);
} else if (isLoadError && offset == 0) { // 加载失败(第一页)
mLoadingView.setVisibility(View.GONE);
mNoDataView.setVisibility(View.GONE);
mNoConnView.setVisibility(View.VISIBLE);
mListRv.setVisibility(View.GONE);
mRefreshLayout.setRefreshing(false);
} else if (offset == 0 && curListSize == 0) { // 数据数据为空
mLoadingView.setVisibility(View.GONE);
mNoDataView.setVisibility(View.VISIBLE);
mNoConnView.setVisibility(View.GONE);
mListRv.setVisibility(View.GONE);
mRefreshLayout.setRefreshing(false);
} else { // 正常加载
mLoadingView.setVisibility(View.GONE);
mNoDataView.setVisibility(View.GONE);
mNoConnView.setVisibility(View.GONE);
mListRv.setVisibility(View.VISIBLE);
mRefreshLayout.setRefreshing(false);
}
}
}
}

View File

@ -0,0 +1,144 @@
package com.gh.gamecenter.baselist;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
import com.gc.materialdesign.views.ProgressBarCircularIndeterminate;
import com.gh.base.fragment.BaseFragment;
import com.gh.common.view.VerticalItemDecoration;
import com.gh.gamecenter.R;
import java.util.List;
import butterknife.BindView;
import butterknife.OnClick;
/**
* Created by khy on 2/12/17.
*/
public abstract class ListFragment<T> extends BaseFragment implements
Observer<List<T>>,
OnListLoadListener,
SwipeRefreshLayout.OnRefreshListener {
@BindView(R.id.list_rv)
RecyclerView mListRv;
@BindView(R.id.list_refresh)
SwipeRefreshLayout mListRefresh;
@BindView(R.id.list_loading)
ProgressBarCircularIndeterminate mListLoading;
@BindView(R.id.reuse_no_connection)
LinearLayout mReuseNoConn;
@BindView(R.id.reuse_none_data)
LinearLayout mReuseNoData;
private LinearLayoutManager mLayoutManager;
protected ListViewModel mListViewModel;
protected abstract BaseListAdapter provideListAdapter();
@Override
protected int getLayoutId() {
return R.layout.fragment_list_base;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ListViewModel.Factory factory = new ListViewModel.Factory(getActivity().getApplication(), this);
mListViewModel = ViewModelProviders.of(this, factory).get(ListViewModel.class);
mListViewModel.getObsListData().observe(this, this);
mListViewModel.load(LoadType.nomarl);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mListRefresh.setOnRefreshListener(this);
mLayoutManager = new LinearLayoutManager(getContext());
mListRv.setLayoutManager(mLayoutManager);
mListRv.setAdapter(provideListAdapter());
mListRv.addItemDecoration(new VerticalItemDecoration(getContext(), 8, true));
mListRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (mLayoutManager.findLastVisibleItemPosition() == provideListAdapter().getItemCount() - 1 &&
RecyclerView.SCROLL_STATE_IDLE == newState)
mListViewModel.load(LoadType.nomarl);
}
});
}
protected int getListOffset() {
return mListViewModel == null ? 0 : mListViewModel.getListOffset();
}
@Override
public void onRefresh() {
mListViewModel.load(LoadType.refresh);
}
@Override
public void onChanged(@Nullable List<T> ts) {
provideListAdapter().provideListData(ts);
}
@OnClick(R.id.reuse_no_connection)
public void onClick(View view) {
mReuseNoConn.setVisibility(View.GONE);
mReuseNoData.setVisibility(View.GONE);
mListLoading.setVisibility(View.VISIBLE);
mListRv.setVisibility(View.GONE);
mListRefresh.setRefreshing(false);
mListViewModel.load(LoadType.refresh);
}
@Override
public void onLoadDone() {
mReuseNoConn.setVisibility(View.GONE);
mReuseNoData.setVisibility(View.GONE);
mListLoading.setVisibility(View.GONE);
mListRv.setVisibility(View.VISIBLE);
mListRefresh.setRefreshing(false);
}
@Override
public void onLoadError() {
mReuseNoConn.setVisibility(View.VISIBLE);
mReuseNoData.setVisibility(View.GONE);
mListLoading.setVisibility(View.GONE);
mListRv.setVisibility(View.GONE);
mListRefresh.setRefreshing(false);
}
@Override
public void onLoadEmpty() {
mReuseNoConn.setVisibility(View.GONE);
mReuseNoData.setVisibility(View.VISIBLE);
mListLoading.setVisibility(View.GONE);
mListRv.setVisibility(View.GONE);
mListRefresh.setRefreshing(false);
}
@Override
public void onLoadMoreError() {
provideListAdapter().loadChange(LoadStatus.error);
}
@Override
public void onLoadOver() {
provideListAdapter().loadChange(LoadStatus.over);
}
}

View File

@ -0,0 +1,123 @@
package com.gh.gamecenter.baselist;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import com.gh.gamecenter.retrofit.Response;
import java.util.ArrayList;
import java.util.List;
import retrofit2.HttpException;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Created by khy on 2/12/17.
*/
public class ListRepository<T> {
public final static int PAGE_SIZE = 20;
private MutableLiveData<List<T>> mListLiveData;
private List<T> mCacheListData;
private OnListLoadListener mLoadListener;
private boolean mIsLoading;
private boolean mIsOver;
private boolean mIsNetworkError;
private int mListOffset;
public ListRepository(OnListLoadListener loadListener) {
mListOffset = 0;
mLoadListener = loadListener;
mCacheListData = new ArrayList<>();
mListLiveData = new MutableLiveData<>();
}
private void loadData() {
Observable<List<T>> listObservable = mLoadListener.provideDataObservable();
if (mIsLoading || mIsOver || mIsNetworkError || listObservable == null) return;
mIsLoading = true;
listObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<List<T>>() {
@Override
public void onResponse(List<T> response) {
super.onResponse(response);
int size = response.size();
if (size == 0) {
mLoadListener.onLoadEmpty();
} else {
if (size < PAGE_SIZE) {
mIsOver = true;
mLoadListener.onLoadOver();
}
mIsLoading = false;
mIsNetworkError = false;
mListOffset += size;
mLoadListener.onLoadDone();
cacheAndNotifyListData(response);
}
}
@Override
public void onFailure(HttpException e) {
super.onFailure(e);
mIsLoading = false;
mIsNetworkError = true;
if (mListOffset > 0) {
mLoadListener.onLoadMoreError();
} else {
mLoadListener.onLoadError();
}
}
});
}
private void resetOffset() {
mListOffset = 0;
mIsOver = false;
mIsNetworkError = false;
mCacheListData.clear();
}
protected void load(LoadType loadType) {
if (loadType == null) loadType = LoadType.nomarl;
switch (loadType) {
case refresh:
resetOffset();
break;
case retry:
mIsNetworkError = false;
break;
}
loadData();
}
protected int getListOffset() {
return mListOffset;
}
private void cacheAndNotifyListData(List<T> listData) {
mCacheListData.addAll(listData);
mListLiveData.postValue(mCacheListData);
}
public LiveData<List<T>> getListLiveData() {
if (mCacheListData != null) {
mListLiveData.postValue(mCacheListData);
}
return mListLiveData;
}
}

View File

@ -0,0 +1,61 @@
package com.gh.gamecenter.baselist;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.support.annotation.NonNull;
import java.util.List;
/**
* Created by khy on 2/12/17.
*/
public class ListViewModel<T> extends AndroidViewModel {
private ListRepository mRepository;
private LiveData<List<T>> mLiveListData;
public ListViewModel(Application application, ListRepository repository) {
super(application);
mRepository = repository;
mLiveListData = mRepository.getListLiveData();
}
public LiveData<List<T>> getObsListData() {
return mLiveListData;
}
public void load(LoadType loadType) {
mRepository.load(loadType);
}
public int getListOffset() {
return mRepository.getListOffset();
}
public static class Factory extends ViewModelProvider.NewInstanceFactory {
@NonNull
private final Application mApplication;
private final ListRepository mRepository;
public Factory(@NonNull Application application, OnListLoadListener loadListener) {
mApplication = application;
mRepository = new ListRepository(loadListener);
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
//noinspection unchecked
return (T) new ListViewModel(mApplication, mRepository);
}
}
}

View File

@ -0,0 +1,22 @@
package com.gh.gamecenter.baselist;
/**
* Created by khy on 4/12/17.
*/
public enum LoadStatus {
error("error"),
over("over"),
refresh("refresh"),
retry("retry");
private String mStatus;
LoadStatus(String status) {
mStatus = status;
}
}

View File

@ -0,0 +1,20 @@
package com.gh.gamecenter.baselist;
/**
* Created by khy on 4/12/17.
*/
public enum LoadType {
nomarl("nomarl"),
refresh("refresh"),
retry("retry");
private String mStatus;
LoadType(String status) {
mStatus = status;
}
}

View File

@ -0,0 +1,29 @@
package com.gh.gamecenter.baselist;
import java.util.List;
import rx.Observable;
/**
* Created by khy on 2/12/17.
*/
public interface OnListLoadListener {
void onLoadDone();
void onLoadError();
void onLoadMoreError();
void onLoadEmpty();
void onLoadOver();
/**
* 列表数据接口 先放这里吧
* @param <T>
* @return
*/
<T> Observable<List<T>> provideDataObservable();
}

View File

@ -1,259 +0,0 @@
package com.gh.gamecenter.baselist;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.gh.base.OnListClickListener;
import com.gh.common.constant.ItemViewType;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.ImageUtils;
import com.gh.common.util.NewsUtils;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder;
import com.gh.gamecenter.adapter.viewholder.NewsImage1ViewHolder;
import com.gh.gamecenter.adapter.viewholder.NewsImage2ViewHolder;
import com.gh.gamecenter.adapter.viewholder.NewsImage3ViewHolder;
import com.gh.gamecenter.entity.NewsEntity;
import com.gh.gamecenter.manager.VisitManager;
import com.gh.gamecenter.retrofit.JSONObjectResponse;
import com.gh.gamecenter.retrofit.RetrofitManager;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Created by khy on 10/11/17.
* 测试例子
*/
public class TestAdapter extends BaseListAdapter {
private OnListClickListener mListListener;
private BaseListPresenterImpl mListPresenter;
private List<NewsEntity> mEntityList;
private boolean mIsLoadOver;
private boolean mIsNetworkError;
public TestAdapter(Context context, BaseListPresenterImpl objectListPresenter, OnListClickListener listListener) {
super(context);
mEntityList = new ArrayList<>();
this.mListPresenter = objectListPresenter;
this.mListListener = listListener;
}
@SuppressWarnings("unchecked")
@Override
protected <T> void provideListData(List<T> listData) {
if (listData.size() < BaseListPresenterImpl.PAGE_SIZE) {
mIsLoadOver = true;
}
mIsNetworkError = false;
mEntityList.addAll((Collection<? extends NewsEntity>) listData);
notifyDataSetChanged();
}
@Override
protected void loadError(Throwable e) {
mIsNetworkError = true;
notifyItemChanged(getItemCount() - 1);
}
@Override
protected void refreshData() {
mEntityList.clear();
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (position == mEntityList.size()) {
return ItemViewType.LOADING;
}
NewsEntity newsEntity = mEntityList.get(position);
if ("4x3".equals(newsEntity.getThumbnail().getType()) && newsEntity.getThumbnail().getUrl().size() == 3) {
return ItemViewType.NEWS_IMAGE2;
}
if ("3x1".equals(newsEntity.getThumbnail().getType())) {
return ItemViewType.NEWS_IMAGE3;
}
return ItemViewType.NEWS_IMAGE1;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case ItemViewType.NEWS_IMAGE1:
view = mLayoutInflater.inflate(R.layout.news_image1_item, parent, false);
return new NewsImage1ViewHolder(view, mEntityList, mListListener);
case ItemViewType.NEWS_IMAGE2:
view = mLayoutInflater.inflate(R.layout.news_image2_item, parent, false);
return new NewsImage2ViewHolder(view, mEntityList, mListListener);
case ItemViewType.NEWS_IMAGE3:
view = mLayoutInflater.inflate(R.layout.news_image3_item, parent, false);
return new NewsImage3ViewHolder(view, mEntityList, mListListener);
case ItemViewType.LOADING:
return new FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false));
default:
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ItemViewType.NEWS_IMAGE1:
initNewsImage1ViewHolder((NewsImage1ViewHolder) holder, position);
break;
case ItemViewType.NEWS_IMAGE2:
initNewsImage2ViewHolder((NewsImage2ViewHolder) holder, position);
break;
case ItemViewType.NEWS_IMAGE3:
initNewsImage3ViewHolder((NewsImage3ViewHolder) holder, position);
break;
case ItemViewType.LOADING:
initFooterViewHolder((FooterViewHolder) holder);
break;
}
}
@Override
public int getItemCount() {
return mEntityList.size() + 1;
}
private void initNewsImage1ViewHolder(final NewsImage1ViewHolder viewHolder, int position) {
final NewsEntity newsEntity = mEntityList.get(position);
ImageUtils.Companion.display(viewHolder.thumb, newsEntity.getThumbnail().getUrl().get(0));
viewHolder.title.setText(newsEntity.getTitle());
int views = newsEntity.getViews();
if (views == 0) {
viewHolder.read.setVisibility(View.GONE);
} else {
viewHolder.read.setVisibility(View.VISIBLE);
viewHolder.read.setText(String.format(Locale.getDefault(), "阅读 %d", views));
}
NewsUtils.setNewsType(viewHolder.type, newsEntity.getType(), newsEntity.getPriority(), position);
}
private void initNewsImage2ViewHolder(final NewsImage2ViewHolder viewHolder, int position) {
final NewsEntity newsEntity = mEntityList.get(position);
DisplayMetrics outMetrics = new DisplayMetrics();
int width = (outMetrics.widthPixels - DisplayUtils.dip2px(mContext, 56)) / 3;
int height = (int) (width * 3 / 4f);
LinearLayout.LayoutParams lparams1 = new LinearLayout.LayoutParams(width, height);
viewHolder.thumb1.setLayoutParams(lparams1);
LinearLayout.LayoutParams lparams2 = new LinearLayout.LayoutParams(width, height);
lparams2.leftMargin = DisplayUtils.dip2px(mContext, 8);
viewHolder.thumb2.setLayoutParams(lparams2);
LinearLayout.LayoutParams lparams3 = new LinearLayout.LayoutParams(width, height);
lparams3.leftMargin = DisplayUtils.dip2px(mContext, 8);
viewHolder.thumb3.setLayoutParams(lparams3);
viewHolder.title.setText(newsEntity.getTitle());
ImageUtils.Companion.display(viewHolder.thumb1, newsEntity.getThumbnail().getUrl().get(0));
ImageUtils.Companion.display(viewHolder.thumb2, newsEntity.getThumbnail().getUrl().get(1));
ImageUtils.Companion.display(viewHolder.thumb3, newsEntity.getThumbnail().getUrl().get(2));
int views = newsEntity.getViews();
if (views == 0) {
viewHolder.read.setVisibility(View.GONE);
} else {
viewHolder.read.setVisibility(View.VISIBLE);
viewHolder.read.setText(String.format(Locale.getDefault(), "阅读 %d", views));
}
NewsUtils.setNewsType(viewHolder.type, newsEntity.getType(), newsEntity.getPriority(), position);
}
private void initNewsImage3ViewHolder(final NewsImage3ViewHolder viewHolder, int position) {
final NewsEntity newsEntity = mEntityList.get(position);
viewHolder.title.setText(newsEntity.getTitle());
ImageUtils.Companion.getInstance().display(viewHolder.thumb, newsEntity.getThumbnail().getUrl().get(0),
mContext.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(mContext, 40));
int views = newsEntity.getViews();
if (views == 0) {
viewHolder.read.setVisibility(View.GONE);
} else {
viewHolder.read.setVisibility(View.VISIBLE);
viewHolder.read.setText(String.format(Locale.getDefault(), "阅读 %d", views));
}
NewsUtils.setNewsType(viewHolder.type, newsEntity.getType(), newsEntity.getPriority(), position);
}
private void initFooterViewHolder(FooterViewHolder viewHolder) {
viewHolder.initItemPadding();
if (mIsNetworkError) {
viewHolder.loading.setVisibility(View.GONE);
viewHolder.hint.setText(R.string.loading_failed_retry);
viewHolder.itemView.setClickable(true);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mIsNetworkError = false;
notifyItemChanged(getItemCount() - 1);
mListPresenter.retryLoad();
}
});
} else if (mIsLoadOver) {
viewHolder.loading.setVisibility(View.GONE);
viewHolder.hint.setText(R.string.loading_complete);
viewHolder.itemView.setClickable(false);
} else {
viewHolder.loading.setVisibility(View.VISIBLE);
viewHolder.hint.setText(R.string.loading);
viewHolder.itemView.setClickable(false);
}
}
// 统计新闻阅读量
public void statNewsViews(final NewsEntity newsEntity, final int position) {
RetrofitManager.getInstance(mContext).getData().postNewsViews(newsEntity.getId())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new JSONObjectResponse() {
@Override
public void onResponse(JSONObject response) {
if (response.length() != 0) {
try {
if ("success".equals(response.getString("status"))) {
newsEntity.setViews(newsEntity.getViews() + 1);
notifyItemChanged(position);
// 更新okhttp缓存数据
VisitManager.updateOkhttpCache(mContext, newsEntity.getId());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
}
}

View File

@ -1,86 +0,0 @@
package com.gh.gamecenter.baselist;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.DataUtils;
import com.gh.common.util.StringUtils;
import com.gh.common.view.VerticalItemDecoration;
import com.gh.gamecenter.NewsDetailActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.NewsEntity;
import com.gh.gamecenter.eventbus.EBNetworkState;
import com.gh.gamecenter.retrofit.RetrofitManager;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import rx.Observable;
/**
* Created by khy on 6/11/17.
* 测试例子
*/
public class TestFragment extends BaseListFragment {
private TestAdapter mAdapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new TestAdapter(getContext(), mListPresenter, this);
mListPresenter.load();
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
RecyclerView recyclerView = view.findViewById(R.id.list_rv);
recyclerView.addItemDecoration(new VerticalItemDecoration(getContext(), 8, true));
}
@Override
protected BaseListAdapter provideAdapter() {
return mAdapter;
}
@SuppressWarnings("unchecked")
@Override
public Observable<List<NewsEntity>> provideDataObservable() {
return RetrofitManager.getInstance(getContext()).getApi().getZiXun(mListPresenter.getListOffset());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBNetworkState busNetworkState) {
if (busNetworkState.isNetworkConnected()) {
}
}
@SuppressWarnings("unchecked")
@Override
public void onListClick(View view, int position, Object data) {
List<NewsEntity> newsList = (List<NewsEntity>) data;
NewsEntity newsEntity = newsList.get(position);
Map<String, Object> kv = new HashMap<>();
kv.put("名字", newsEntity.getTitle());
kv.put("位置", String.valueOf(position + 1));
DataUtils.onEvent(getContext(), "点击", "资讯-资讯", kv);
DataCollectionUtils.uploadClick(getContext(), "列表", "资讯-资讯", newsEntity.getTitle());
//统计阅读量
mAdapter.statNewsViews(newsEntity, position);
NewsDetailActivity.startNewsDetailActivity(getContext(), newsEntity, StringUtils.buildString("(资讯:资讯[" + position + "])"));
}
}

View File

@ -8,6 +8,7 @@ import android.view.View;
import com.gh.base.SearchBarHint;
import com.gh.base.fragment.BaseFragment_ViewPager_Checkable;
import com.gh.gamecenter.R;
import com.gh.gamecenter.ask.AskFragment;
import com.gh.gamecenter.eventbus.EBReuse;
import com.gh.gamecenter.eventbus.EBSkip;
import com.gh.gamecenter.eventbus.EBUISwitch;
@ -45,8 +46,9 @@ public class MainFragment extends BaseFragment_ViewPager_Checkable {
View mMessageHintIv;
public static final int INDEX_GAME = 0;
public static final int INDEX_NEWS = 1;
public static final int INDEX_PERSONAL = 2;
public static final int INDEX_ASK = 1;
public static final int INDEX_NEWS = 2;
public static final int INDEX_PERSONAL = 3;
private ArrayList<String> mSearchHintLint;
@Override
@ -67,6 +69,7 @@ public class MainFragment extends BaseFragment_ViewPager_Checkable {
@Override
protected void initFragmentList(List<Fragment> fragments) {
fragments.add(new GameWrapperFragment());
fragments.add(new AskFragment());
fragments.add(new NewsWrapperFragment());
fragments.add(new PersonalFragment());
}